Chapter 7 – Creating some followers

February 23rd, 2010 Leave a comment Go to comments


Implementing a new sort of Pawn

In this tutorial we will explore the implementation of a simple AI. After you master this basic implementation, I strongly suggest you take a look at how Dungeon defense implemented their AI.

We will begin by defining a new unreal script class that we will name FollowerPawn. This will represent the other member of your squad. As a simple observation, you would be right to say that this pawn is identical to the basic avatar that you used since chapter 1.

class FollowerPawn extends GamePawn;

simulated event PostBeginPlay()
{
	super.PostBeginPlay();
	SpawnDefaultController();
}

simulated function name GetDefaultCameraMode( PlayerController RequestedBy )
{
    return 'Isometric';
}

defaultproperties
{
	ControllerClass=class'FollowerAIController'

	Begin Object Name=CollisionCylinder
		CollisionRadius=+0034.000000
		CollisionHeight=+0034.000000
		BlockZeroExtent=FALSE
	End Object

	Components.Remove(Sprite)

	Begin Object Class=DynamicLightEnvironmentComponent Name=MyLightEnvironment
		ModShadowFadeoutTime=0.25
		MinTimeBetweenFullUpdates=0.2
		AmbientGlow=(R=.01,G=.01,B=.01,A=1)
		AmbientShadowColor=(R=0.15,G=0.15,B=0.15)
		LightShadowMode=LightShadow_ModulateBetter
		ShadowFilterQuality=SFQ_High
		bSynthesizeSHLight=TRUE
	End Object
	Components.Add(MyLightEnvironment)

    Begin Object Class=SkeletalMeshComponent Name=InitialSkeletalMesh
		CastShadow=true
		bCastDynamicShadow=true
		bOwnerNoSee=false
		LightEnvironment=MyLightEnvironment;
        BlockRigidBody=true;
        CollideActors=true;
        BlockZeroExtent=true;
		BlockNonZeroExtent=TRUE
		bIgnoreControllersWhenNotRendered=TRUE
		bUpdateSkelWhenNotRendered=FALSE
		PhysicsAsset=PhysicsAsset'CH_AnimCorrupt.Mesh.SK_CH_Corrupt_Male_Physics'
		AnimSets(0)=AnimSet'CH_AnimHuman.Anims.K_AnimHuman_AimOffset'
		AnimSets(1)=AnimSet'CH_AnimHuman.Anims.K_AnimHuman_BaseMale'
		AnimTreeTemplate=AnimTree'CH_AnimHuman_Tree.AT_CH_Human'
		SkeletalMesh=SkeletalMesh'CH_IronGuard_Male.Mesh.SK_CH_IronGuard_MaleA'
	End Object

	Mesh=InitialSkeletalMesh;
	Components.Add(InitialSkeletalMesh);
	name='FollowerOne';
}


Implementing a new intelligence

With this basic pawn implementation we will set on to define a controller to help make it move. At some point I do hope he also dances but there is no default UT animations for that ;)

Lets add a new unreal script file and name it FollowerAIController. I like to put the AI in the name so its clear that it is extending GameAIController for future refenrences. NOTE: copy/paste works fine even if text is not all visible.

class FollowerAIController extends GameAIController;

var()   Vector  TempDest;
var     float   OffsetToHero;

function SetOrders(name NewOrders, Controller OrderGiver)
{
	local Actor DestActor;

	`Log("Received order : "@NewOrders);
	if(NewOrders == 'Follow')
	{
		if(IsInState('ScriptedMove'))
		{
			PopState(true);
		}

		DestActor = OrderGiver.Pawn;
		ScriptedRoute = Route(DestActor);
		ScriptedRouteIndex = 0;
		if (ScriptedRoute.RouteList.length == 0)
		{
			//`warn("Invalid route with empty MoveList for scripted move");
			ScriptedMoveTarget = DestActor;
			PushState('ScriptedMove');
		}
		else
		{
			PushState('ScriptedRouteMove');
		}
	}
}

//Overwrite AIController's ScriptedMove state to make use of the NavigationHandle instead of the old way
state ScriptedMove
{
	function bool FindNavMeshPath()
	{
		// Clear cache and constraints (ignore recycling for the moment)
		NavigationHandle.PathConstraintList = none;
		NavigationHandle.PathGoalList = none;

		// Create constraints
		class'NavMeshPath_Toward'.static.TowardGoal( NavigationHandle,ScriptedMoveTarget );
		class'NavMeshGoal_At'.static.AtActor( NavigationHandle, ScriptedMoveTarget );

		// Find path
		return NavigationHandle.FindPath();
	}

	Begin:
		`log("BEGIN STATE SCRIPTEDMOVE");
		// while we have a valid pawn and move target, and
		// we haven't reached the target yet
		if( FindNavMeshPath() )
		{
			NavigationHandle.SetFinalDestination(ScriptedMoveTarget.Location);
			`log("FindNavMeshPath returned TRUE");
			FlushPersistentDebugLines();
			NavigationHandle.DrawPathCache(,TRUE);

			while( Pawn != None && ScriptedMoveTarget != None && !Pawn.ReachedDestination(ScriptedMoveTarget) )
			{				
				if( NavigationHandle.ActorReachable( ScriptedMoveTarget) )
				{
					// then move directly to the actor
					MoveTo( ScriptedMoveTarget.Location,ScriptedFocus, OffsetToHero, true );
				}
				else
				{
					// move to the first node on the path
					if( NavigationHandle.GetNextMoveLocation( TempDest, Pawn.GetCollisionRadius()) )
					{
						// suggest move preparation will return TRUE when the edge's
					    // logic is getting the bot to the edge point
							// FALSE if we should run there ourselves
						if (!NavigationHandle.SuggestMovePreparation( TempDest,self))
						{
							MoveTo( TempDest, ScriptedFocus, OffsetToHero, true );						
						}
					}
				}
			}
		}
		else
		{
			//give up because the nav mesh failed to find a path
			`warn("FindNavMeshPath failed to find a path to"@ScriptedMoveTarget);
			ScriptedMoveTarget = None;
		}   


	`log("POPPING STATE!");
	Pawn.ZeroMovementVariables();
	// return to the previous state
	PopState();
}


DefaultProperties
{
	OffsetToHero=75;
}


Notes on the basis of the approach

I propose something akin to the UT bot’s SetOrders function so that you can be able to search for similar code in the UT game implementation. As such, I used a scheme like the one for controlling bots in team matches. It may not suit you for all your needs, but it will definitely make it easier to understand the principles of controlling the different basic behaviors of UT bots. If you are not familiar with the concept of state machines, I strongly suggest you get the book Programming Game AI by Example of Math Buckland. I gave this book to read to all my new employees to read. It generally makes it easier to understand behavior programming when you come from say a more IT background.

In the SetOrders function, we have only one command that the bot understand and its the ‘follow’ instruction. If the bot is asked to follow, it will check if it can navigate with path nodes first. if no such nodes exists, he will use the navmesh. You should notice that the bot will not come closer than the OffsetToHero variable defined in the DefaultProperties. The path node navigation is already defined in AIController so no need to redefine it here. What we added was a state to navigate on the navmesh. Now I do not have any merit here, I merely almost copied the example on the UDK page for navmesh implementation. Of note, this code will need further testing and fixing.

 

Chapter 7 spawning the followers

 

 

 

Update me when site is updated
Share and Enjoy:
  • Print
  • Digg
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • Slashdot
  • PDF
  • Twitter
  1. Nestor
    May 12th, 2010 at 16:29 | #1

    Nice tutorial. I have some issues when trying to increase the size of the CollisionCylinder (for bigger pawns), if I make it bigger 64×64, the pathfinding stop working, I try a solution found on the epic forums (http://forums.epicgames.com/showpost.php?p=27009970&postcount=3) that says that increasing the NavMeshGen_MaxPolyHeight in Scout.uc will solve the problem but it didn’t work. Any advice on making navmeshses with bigger characters?

  2. Roychr
    May 13th, 2010 at 19:17 | #2

    You have to be careful about resizing that cylinder, there are specific sizes for pawns, vehicles and other that the system will treat as small, big or medium. If you change those, you will have to tweak the navigation function, you should check on the UDK documentation around here : http://udn.epicgames.com/Three/NavigationMeshTechnicalGuide.html

  3. RabbidFerret
    November 25th, 2010 at 12:56 | #3

    Thanks for this tutorial! We are having a slight problem where it seems as though there is some sort of debug mode for pathing pylons that has been turned on during gameplay. It looks like this: http://img137.imageshack.us/img137/4130/pathingissue.jpg

    Every pylon with bots in them flashes their debug mesh on the screen. Taking the bots away solves the issue, but obviously that is not the solution. Any ideas?

    • Roychr
      November 25th, 2010 at 13:25 | #4

      You should not call the debug functions (this is a UDK function) that shows debug informations for paths of AI and nodes and such. Its just called by default in my code somewhere. Look into the code its pretty obvious.

  1. February 23rd, 2010 at 16:06 | #1
You must be logged in to post a comment.