Friday, December 2

Unrealscript Overview Part Three : NPC Controller

However, a pawn is not enough to give you a full NPC. A controller is also needed. Think of this as a model - view - controller set up. The view is handled by the rendering engine iteslf, the model is the unrealscript code for the pawn, and the actual controller is the controller class associated with the Pawn itself. Nearly all NPC or player pawns will have a controller associated with them. In the case of NPCs this will derive from AIController, which derives from GameController. These base classes define some logic for simple pathfollowing which may or may not be useful. In our example, the BubbleController does not use this derived class code.


AI Controllers usually are implemented as State machines. Unrealscript has a state construct that makes this easy. Code enclosed in a named state block is executed only when the object enters that state. The controller has an explicit method for switching state called GotoState(). As this is a simple example I have not overridden any methods inside the state block, but have written short fragments of code that can be executed latentley. Latent functions are Unrealscript speak for functions that run in the background. There are a small group of these available to you (grepping through the Controller, Actor and Pawn classes should reaval them), mostly related to Pawn movmement. Here, the rising state sets the Pawn velocity when it is etered, and then periodically checks to see if the bubble has risen too far. When it has, it enters the Popping state, wherein it stands still for a second, and then is destroyed.


The Destroy() function brings us to another point. Objects derived from actor have a special property in that they are spawned rather than created with the new() operator. In our BubblePawn class we called a base method called SpawnDefaultController(), and set ControllerClass to class'LavaLamp.BubbleController'. SpawnDefaultController will then create an instance of that controller class, and ensure that the controller Poseses (ie controls/owns) the Pawn by calling Posses. It is perfectly theoretially possible to pass a pawn around different controllers to implement different behaviours - although the state mechanism makes this less useful than it might sound.

class BubbleController extends AIController;



var float RisingDistance;

var float ExtraVelocity;



simulated Event SetInitialState() 

{

 ExtraVelocity = FRand() * 1.25;

 bScriptInitialized = true;

 GotoState('Rising');

}



state Rising

{

Begin: 

 Pawn.Velocity = vect(0.0, 0.0, 0.85);

 Pawn.Velocity.Z += ExtraVelocity;

RisingLoop:

 if (Pawn.Location.Z  > RisingDistance)  {

  StopLatentExecution();

  GotoState('Popping');

 }

 Sleep(1);

 goto 'RisingLoop';

}



state Popping

{

Begin:

 Pawn.Velocity = vect(0.0, 0.0, 0.0);

PoppingLoop:

 Sleep(1);

 Pawn.Destroy();

 Pawn = None;

 Destroy();

 goto 'PoppingLoop';

}





defaultproperties

{

 RisingDistance = 2048.0

}

No comments: