EDIT:
Good news is the bots will chase the player down the level. It's navigating up the platforms that appears to be the problem.
The bots in our test levels will not follow the player up the platforms. We have tried numerous things like blocking volumes for the two sides of the 2D plane, laddervolumes with paths, adding in more pathnodes, increasing bot jump height. Nothing works.
Here is an example of what is happening:
http://www.youtube.com/watch?v=qkugk-crsbI
Here is the code we have been looking at thus far:
Bot code:
Player controller code:Code:class UT2DBot extends UTBot; //Should never strafe function bool ShouldStrafeTo(Actor WayPoint) { return true; } /* //Adjust towards, not around function bool AdjustAround(Pawn Other) { SetAdjustLocation( Adj, TRUE ); return true; } */ // function bool AdjustAround(Pawn Other) { local vector VelDir, OtherDir, SideDir, HitLocation, HitNormal, Adj; if ( !InLatentExecution(LATENT_MOVETOWARD) ) return false; VelDir = Normal(MoveTarget.Location - Pawn.Location); VelDir.Z = 0; OtherDir = Other.Location - Pawn.Location; OtherDir.Z = 0; OtherDir = Normal(OtherDir); if ( (VelDir Dot OtherDir) > 0.8 ) { SideDir.X = VelDir.Y; SideDir.Y = -1 * VelDir.X; if ( (SideDir Dot OtherDir) > 0 ) SideDir *= -1; Adj = Pawn.Location + 3 * Other.GetCollisionRadius() * (0.5 * VelDir + SideDir); // make sure adjust location isn't through a wall if (Trace(HitLocation, HitNormal, Adj, Pawn.Location, true) != None) { Adj = HitLocation; } SetAdjustLocation( Adj, TRUE ); return true; } else { return false; } } // //No need to clear paths when players don't collide function ClearPathFor(Controller C) { } /* //GetAdjustedAimFor function rotator GetAdjustedAimFor(Weapon W, vector StartFireLoc) { return Rotation; } */ //Make AI more aggressive function bool FireWeaponAt(Actor A) { Focus = A; return super.FireWeaponAt(A); } state Fallback { function bool FireWeaponAt(Actor A) { Focus = A; return super.FireWeaponAt(A); } } //DefaultProperties defaultproperties { }
Scout code:Code:class UT2DController_Player extends UTPlayerController; //Camera Calculations var vector LastViewLoc; var vector CameraOffset; var float LookingOffset; //Aiming var vector Look; var float LookSensitivity, TurningThreshold; //Simulating analog jump and crouch var float AnalogJumpThreshold, AnalogDuckThreshold; var bool bAnalogJumped; //Ease slowly into crouch-looking var float DuckTransitionTime; var float BeganDucking; var float StoppedDucking; //Face the direction of newly spawned pawns event Possess(Pawn inPawn, bool bVehicleTransition) { Look = vector(inPawn.Rotation); super.Possess(inPawn, bVehicleTransition); } //Force third-person function SetBehindView(bool bNewBehindView) { super.SetBehindView(true); } //Third-person camera simulated event GetPlayerViewPoint(out vector POVLocation, out Rotator POVRotation) { local UT2DPawn P; local rotator rot; local vector vHeadOffset, vAimingOffset; local float scale; if(ViewTarget != none) { P = UT2DPawn(ViewTarget); if(!IsInState('Dead') && !IsInState('RoundEnded') && P != none && P.IsLocallyControlled()) { if(IsInState('Spectating')) { rot = P.Rotation; rot.Pitch = P.RemoteViewPitch << 8; Look = vector(rot); } vHeadOffset.Z = P.HeadOffset; LastViewLoc = P.Location + vHeadOffset; scale = 1; if(abs(Look.X) < TurningThreshold) scale += abs(Look.X) / TurningThreshold - 1; if(Look.X < 0) scale *= -1; vAimingOffset.X = LookingOffset * Scale; //Ducking if(BeganDucking != 0) scale = fmin(WorldInfo.TimeSeconds - BeganDucking, DuckTransitionTime) / DuckTransitionTime; else scale = 2 - fmin(WorldInfo.TimeSeconds - StoppedDucking, DuckTransitionTime) / DuckTransitionTime; vAimingOffset.Z += P.EyeHeight - P.default.EyeHeight + LookingOffset * Look.Z * scale; LastViewLoc.Y = 0; POVLocation = LastViewLoc + vAimingOffset + CameraOffset; } else { if(!ViewTarget.IsA('UTGib')) LastViewLoc = ViewTarget.Location; if(ViewTarget == self) { if(LastViewLoc.Y == 0) { LastViewLoc += CameraOffset * 2; SetLocation(LastViewLoc); } bCollideWorld = true; POVLocation = LastViewLoc; LastViewLoc -= CameraOffset * 2; } else { LastViewLoc.Y = 0; POVLocation = LastViewLoc + CameraOffset; } } } else POVLocation = LastViewLoc + CameraOffset; POVRotation = rotator(-CameraOffset); StopViewShaking(); if(CameraEffect != none) CameraEffect.UpdateLocation(POVLocation, POVRotation, GetFOVAngle()); } //GetAdjustedAimFor function rotator GetAdjustedAimFor(Weapon W, vector StartFireLoc) { return Rotation; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ HERE BE DRAGONS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //UpdateRotation function UpdateRotation(float DeltaTime) { local rotator DeltaRot; //Forget look inversion if(PlayerInput.bInvertMouse) PlayerInput.aLookup = -PlayerInput.aLookup; if(PlayerInput.bInvertTurn) PlayerInput.aTurn = -PlayerInput.aTurn; //Fake out UpdateRotation to rotate as our aiming system expects Look.Z += PlayerInput.aLookup * LookSensitivity; Look.X += PlayerInput.aTurn * LookSensitivity; Look = normal(Look); DeltaRot = rotator(Look) - Rotation; PlayerInput.aLookUp = DeltaRot.Pitch; PlayerInput.aTurn = DeltaRot.Yaw; super.UpdateRotation(DeltaTime); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ HERE BE DRAGONS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ //PlayerWalking state PlayerWalking { //PlayerMove function PlayerMove(float DeltaTime) { local byte bOrigDuck; //Remember the original duck bOrigDuck = bDuck; //Analog up to jump if(PlayerInput.aBaseY > AnalogJumpThreshold) { if(!bAnalogJumped) bPressedJump = true; bAnalogJumped = true; } else bAnalogJumped = false; //Analog down to duck if(PlayerInput.aBaseY < AnalogDuckThreshold) bDuck = 1; if(Pawn.Physics == PHYS_Walking && BeganDucking == 0 && bDuck == 1) { BeganDucking = WorldInfo.TimeSeconds; if(BeganDucking - StoppedDucking < DuckTransitionTime) BeganDucking -= DuckTransitionTime - (BeganDucking - StoppedDucking); } else if(BeganDucking != 0 && (bDuck == 0 || Pawn.Physics != PHYS_Walking)) { StoppedDucking = WorldInfo.TimeSeconds; if(StoppedDucking - BeganDucking < DuckTransitionTime) StoppedDucking -= DuckTransitionTime - (StoppedDucking - BeganDucking); BeganDucking = 0; } PlayerInput.aForward = 0; //Move based on side-scrolling Pawn.SetRotation(rot(0, -16384, 0)); super.PlayerMove(DeltaTime); //Forget analog duck bDuck = bOrigDuck; } } //PlayerSwimming state PlayerSwimming { //PlayerMove function PlayerMove(float DeltaTime) { //Analog up to jump if(PlayerInput.aBaseY > AnalogJumpThreshold) { if(!bAnalogJumped) bPressedJump = true; bAnalogJumped = true; } else bAnalogJumped = false; //Move based on side-scrolling PlayerInput.aUp += PlayerInput.aForward; PlayerInput.aForward = 0; SetRotation(rot(0, -16384, 0)); super.PlayerMove(DeltaTime); SetRotation(Pawn.Rotation); } } //PlayerClimbing state PlayerClimbing { //PlayerMove function PlayerMove(float DeltaTime) { //Move based on side-scrolling PlayerInput.aUp += PlayerInput.aForward; PlayerInput.aForward = 0; SetRotation(rot(0, -16384, 0)); super.PlayerMove(DeltaTime); SetRotation(Pawn.Rotation); } } //PlayerFlying state PlayerFlying { //PlayerMove function PlayerMove(float DeltaTime) { //Move based on side-scrolling PlayerInput.aUp += PlayerInput.aForward; PlayerInput.aForward = 0; SetRotation(rot(0, -16384, 0)); super.PlayerMove(DeltaTime); SetRotation(Pawn.Rotation); } } //PlayerWaiting auto state PlayerWaiting { //PlayerMove function PlayerMove(float DeltaTime) { //Move based on side-scrolling PlayerInput.aUp += PlayerInput.aForward; PlayerInput.aForward = 0; SetRotation(rot(0, -16384, 0)); super.PlayerMove(DeltaTime); } } state WaitingForPawn { //PlayerMove function PlayerMove(float DeltaTime) { //Move based on side-scrolling PlayerInput.aUp += PlayerInput.aForward; PlayerInput.aForward = 0; SetRotation(rot(0, -16384, 0)); super.PlayerMove(DeltaTime); } } state Dead { //PlayerMove function PlayerMove(float DeltaTime) { //Move based on side-scrolling PlayerInput.aUp += PlayerInput.aForward; PlayerInput.aForward = 0; SetRotation(rot(0, -16384, 0)); super.PlayerMove(DeltaTime); } } state InQueue { //PlayerMove function PlayerMove(float DeltaTime) { //Move based on side-scrolling PlayerInput.aUp += PlayerInput.aForward; PlayerInput.aForward = 0; SetRotation(rot(0, -16384, 0)); super.PlayerMove(DeltaTime); } } //unreliable server function ServerViewSelf() //{ // SetLocation(LastViewLoc + CameraOffset * 2); // SetViewTarget(self); //} state Spectating { //PlayerMove function PlayerMove(float DeltaTime) { //Move based on side-scrolling PlayerInput.aUp += PlayerInput.aForward; PlayerInput.aForward = 0; SetRotation(rot(0, -16384, 0)); super.PlayerMove(DeltaTime); } exec function StartAltFire(optional byte FireModeNum) { ServerViewSelf(); } } //DefaultProperties defaultproperties { //Cheating //CheatClass=class'UT2DCheatManager' //Camera bBehindView=true; CameraOffset=(X=0,Y=700,Z=32) LookingOffset=0 //Aiming Look=(X=1,Y=1,Z=1) LookSensitivity=0.00025 TurningThreshold=0.85 //Analog Simulation AnalogJumpThreshold=1000 AnalogDuckThreshold=-1000 //Ducking DuckTransitionTime=0.3 BeganDucking=0 StoppedDucking=-0.3
Pawn code:Code:class UT2DScout extends UTScout; //DefaultProperties defaultproperties { MinNumPlayerStarts=2 TestJumpZ=750 MaxJumpHeight=750 MaxDoubleJumpHeight=0 PrototypePawnClass=class'UT2D.UT2DPawn' }
Some people have suggested making our own AI. Realistically, for people not so experienced with C++ and UnrealScript - how difficult will this be?Code:class UT2DPawn extends UTPawn; var rotator LastRotation; var float LastTrans; var float WallDodgeSpeed; var float WallDodgeSpeedZ; //Tick simulated function Tick(float DeltaTime) { local vector loc; local rotator rot; local int deltaPitch; local bool bTurned; super.Tick(DeltaTime); if(bBlockActors) SetCollision(bCollideActors, false); //Clamp to Y = 0. loc = Location; loc.Y = 0; SetLocation(loc); //Swimming/Flying Fix if(Physics == PHYS_Swimming || Physics == PHYS_Flying) { rot = Rotation; rot.Pitch = 0; SetRotation(rot); } //Bot Hacks if(UTBot(Controller) == none) return; //rot = rotator(Controller.FocalPoint - (Location + vect(0, 0, 1) * HeadOffset)); rot = rotator(Location + vect(0, 0, 1) * HeadOffset); //Clamp to 2D if(abs(rot.Yaw) < 16384) rot.Yaw = 0; else rot.Yaw = 32768; deltaPitch = RotationRate.Pitch * DeltaTime * 3; //If it's looking at the side it wants to if(rot.Yaw == LastRotation.Yaw) { if(abs(rot.Pitch - LastRotation.Pitch) <= deltaPitch) LastRotation = rot; //Just look at an enemy if its close else if(rot.Pitch > LastRotation.Pitch) LastRotation.Pitch += deltaPitch; //Move up towards it else LastRotation.Pitch -= deltaPitch; //Move down towards it } else { if(rot.Pitch >= 0) LastRotation.Pitch += deltaPitch; //Move up towards it else LastRotation.Pitch -= deltaPitch; //Move down towards it } //Turn around if(LastRotation.Pitch > 16384) { LastRotation.Pitch = 32768 - LastRotation.Pitch; bTurned = true; } else if(LastRotation.Pitch < -16384) { LastRotation.Pitch = -32768 - LastRotation.Pitch; bTurned = true; } if(bTurned) { if(abs(LastRotation.Yaw) < 16384) LastRotation.Yaw = 32768; else LastRotation.Yaw = 0; } Controller.SetRotation(LastRotation); /* rot = LastRotation; SetRemoteViewPitch(rot.Pitch); rot.Pitch = 0; SetRotation(rot); */ } //Bot Hack function bool NeedToTurn(vector Target) { return false; } //Bounce into 2D simulated event HitWall(vector HitNormal, Actor Wall, PrimitiveComponent WallComp) { if(HitNormal.X != 0) { HitNormal.Y = 0; HitNormal = normal(HitNormal); } super.HitWall(HitNormal, Wall, WallComp); } //AddVelocity function AddVelocity(vector NewVelocity, vector HitLocation, class<DamageType> damageType, optional TraceHitInfo HitInfo) { local float magnitude; //Clamp to 2D. magnitude = vsize(NewVelocity); NewVelocity.Y = 0; NewVelocity = normal(NewVelocity) * magnitude; super.AddVelocity(NewVelocity, HitLocation, damageType, HitInfo); } //UpdateEyeHeight event UpdateEyeHeight(float DeltaTime) { if(!bIsCrouched) super.UpdateEyeHeight(DeltaTime); } //Dodge function bool Dodge(eDoubleClickDir DoubleClickMove) { local vector TraceStart, TraceEnd, Dir, Cross, HitLocation, HitNormal; local Actor HitActor; local bool bFalling, out; if(bIsCrouched || bWantsToCrouch || (Physics != PHYS_Walking && Physics != PHYS_Falling)) return false; if(Physics == PHYS_Falling) { bFalling = true; if(DoubleClickMove == DCLICK_Left) TraceEnd = vect(1, 0, 0); else if(DoubleClickMove == DCLICK_Right) TraceEnd = vect(-1, 0, 0); TraceStart = Location - (CylinderComponent.CollisionHeight - 16) * vect(0, 0, 1) + TraceEnd * (CylinderComponent.CollisionRadius - 16); TraceEnd = TraceStart + TraceEnd * 40; HitActor = Trace(HitLocation, HitNormal, TraceEnd, TraceStart, false, vect(16, 16, 16)); HitNormal.Y = 0; HitNormal = normal(HitNormal); if((HitActor == none) || (HitNormal.Z < -0.1)) return false; if(!HitActor.bWorldGeometry) { if(!HitActor.bBlockActors) return false; if((Pawn(HitActor) != none) && (Vehicle(HitActor) == none)) return false; } } if(DoubleClickMove == DCLICK_Left) { Dir = vect(-1, 0, 0); Cross = vect(0, 1, 0); } else if(DoubleClickMove == DCLICK_Right) { Dir = vect(1, 0, 0); Cross = vect(0, 1, 0); } if(bFalling) { Velocity = vect(0, 0, 0); Acceleration = vect(0, 0, 0); } out = PerformDodge(DoubleClickMove, Dir, Cross); return out; } //Jump out of water always. function bool CheckWaterJump(out vector WallNormal) { return true; } //Block living things. simulated function SetPawnRBChannels(bool bRagdollMode) { super.SetPawnRBChannels(bRagdollMode); Mesh.SetRBCollidesWithChannel(RBCC_Untitled2, Health > 0); } state FeigningDeath { function AddVelocity(vector NewVelocity, vector HitLocation, class<DamageType> damageType, optional TraceHitInfo HitInfo); } //DefaultProperties defaultproperties { Begin Object Name=WPawnSkeletalMeshComponent AnimTreeTemplate=AnimTree'CH_Allen.Anim.CH_Allen_AnimTree' End Object ControllerClass=class'UT2D.UT2DBot' bCanStrafe=false //Jumping JumpZ=750 MaxMultiJump=0 MultiJumpRemaining=0 MultiJumpBoost=0 OutofWaterZ=1100 MaxJumpHeight=264 MaxDoubleJumpHeight=0 bStopOnDoubleLanding=false DoubleJumpThreshold=160.0 DoubleJumpEyeHeight=43.0 bCanDoubleJump=false bCanCrouch=false SightRadius=5000 }



Reply With Quote


Bookmarks