Sunday, April 18, 2010

Walk your Pawn - Follow Up

If like me or Droganis from the UDK forums you would like your pawn to walk by default and only run on a key press, you can use this.

First follow the "Walk Your Pawn" tutorial if you haven't, then in your PlayerController's HandleWalking function, use this so that we only ask the Pawn to run when bRun is set i.e. when Shift is down.

function HandleWalking()
{
if ( Pawn != None )
Pawn.SetWalking( bRun == 0 );
}

Tuesday, March 2, 2010

Tutorial: Replicating a flashlight's state

I've been trying to understand replication so based on a question on the UDK forums I decided to implement a flash light whose on/off state can be replicated.

First, create a subclass of a SpotLightMovable which will act as the flash light.

class MyFlashLight extends SpotLightMovable
notplaceable;

defaultproperties
{
Begin Object name=SpotLightComponent0
LightColor=(R=255,G=0,B=0)
End Object
bNoDelete=FALSE
}



Next, we will modify our custom pawn by adding the flashlight as a variable.

var MyFlashLight FlashLight;

We also need a variable that we will replicate the flashlight's on/off state.

var repnotify bool bIsFlashlightOn; /// whether the flashlight is on or not

Then in the pawn's PostBeginPlay, we will attach the flash light to the pawn.

simulated function PostBeginPlay()
{
FlashLight = Spawn(class'MyFlashLight', self);
FlashLight.SetBase(self);
FlashLight.LightComponent.SetEnabled(self.default.bIsFlashlightOn);
super.PostBeginPlay();
}


And set the variable's default value in the pawn's default properties.

defaultproperties
{
...
bIsFlashlightOn=true
}


Now we need a way to switch the flashlight on and off. We will set this up via an exec function that will be called when the "R" key is pressed. I added my key bind to the DefaultInput.ini (You might need to make the file writable as its read-only by default).

Add this line at the end of the section marked as Primary default bindings

.Bindings=(Name="R",Command="ToggleFlashlight")

This says that when the R key is pressed the exec function ToggleFlashlight will be called. We will define the function in our cutsom PlayerController subclass.

exec function ToggleFlashlight()
{
MyPawn(Pawn).ToggleFlashlight();
}


This calls the PlayerController's Pawn's ToggleFlashlight function (a mouth-full I know). Now this is where the magic happens. Since we essentially have all the information we need to determine whether the flash light can be turned on or not (without consulting the server) we can do what is nessecary to switch on the flashlight. To do this, we need to mark the funtion with the simulated keyword.

simulated function ToggleFlashlight()
{
bIsFlashlightOn = !bIsFlashlightOn;
FlashLightToggled();
// if we are a remote client, make sure the Server toggles the flashlight
if( Role < Role_Authority )
{
ServerToggleFlashlight();
}
}

We start by changing the value of our variable that keeps track of the flashlight's state.

Next we check whether we are a client - in which case we need to tell the server what we have done by calling a server function. The server function essentially does the same thing as the client version, but on the server.

reliable server function ServerToggleFlashlight()
{
bIsFlashlightOn = !bIsFlashlightOn;
`log("ServerToggleFlashlight: " $ bIsFlashlightOn);
FlashLightToggled();
}


You'll notice that both functions call the FlashLightToggled function. This does the actual state switch.

simulated function FlashLightToggled()
{
if(bIsFlashlightOn)
{
FlashLight.LightComponent.SetEnabled(true);
}
else
{
FlashLight.LightComponent.SetEnabled(false);
}
}


This has taken care of the client that switched the flashlight on/off and the server, but what about the other clients. If you look at the definition of the bIsFlashlightOn variable, its defined as repnotify. What this does is whenever the variable changes, a special function ReplicatedEvent is called.

simulated event ReplicatedEvent(name VarName)
{
if (VarName == 'bIsFlashlightOn')
{
FlashLightToggled();
}
else
{
Super.ReplicatedEvent(VarName);
}
}


This function will be called on all the clients including the client that initiated the whole thing.

We also need a way to tell the server when to send the variable to its clients, this is done through a replication condition in our pawn class like this.

replication
{
// replicated properties
if ( bNetDirty )
bIsFlashlightOn;
}


That about covers it. Here are the full sources for each of the 3 classes we have touched on.

MyFlashlight.uc

class MFlashLight extends SpotLightMovable
notplaceable;


defaultproperties
{
Begin Object name=SpotLightComponent0
LightColor=(R=255,G=0,B=0) /// red so we can see the change
End Object
bNoDelete=FALSE
}

MyPawn.uc

class MyPawn extends UTPawn
config(Game)
notplaceable;

var MyFlashLight FlashLight;
var repnotify bool bIsFlashlightOn; /// whether the flashlight is on or not

replication
{
// replicated properties
if ( bNetDirty )
bIsFlashlightOn;
}

simulated function PostBeginPlay()
{
FlashLight = Spawn(class'KDFlashLight', self);
FlashLight.SetBase(self);
FlashLight.LightComponent.SetEnabled(self.default.bIsFlashlightOn);
super.PostBeginPlay();
}

/**
* Check on various replicated data and act accordingly.
*/
simulated event ReplicatedEvent(name VarName)
{
`log(VarName @ "replicated");
if (VarName == 'bIsFlashlightOn')
{
FlashLightToggled();
`log("bIsFlashlightOn replicated");
}
else
{
Super.ReplicatedEvent(VarName);
}
}

simulated function ToggleFlashlight()
{
bIsFlashlightOn = !bIsFlashlightOn;
`log("ToggleFlashlight: " $ bIsFlashlightOn);
FlashLightToggled();
// if we are a remote client, make sure the Server Set's toggles the flashlight
`log("Role:" @ Role);
if( Role < Role_Authority )
{
ServerToggleFlashlight();
}
}

reliable server function ServerToggleFlashlight()
{
bIsFlashlightOn = !bIsFlashlightOn;
`log("ServerToggleFlashlight: " $ bIsFlashlightOn);
FlashLightToggled(!bIsFlashlightOn);
}

simulated function FlashLightToggled()
{
if(bIsFlashlightOn)
{
FlashLight.LightComponent.SetEnabled(true);
}
else
{
FlashLight.LightComponent.SetEnabled(false);
}
}

defaultproperties
{
bIsFlashlightOn=true
}

MyPlayerController.uc

class MyPlayerController extends UTPlayerController;

exec function ToggleFlashlight()
{
MyPawn(Pawn).ToggleFlashlight();
}

state Dead
{
function EndState(name NextStateName)
{
SetBehindView(default.bBehindView);
}
}

defaultproperties
{
bBehindView = true
}

Tuesday, February 9, 2010

Walk your Pawn

So UTPawn, which many UDK users are subclassing does not walk, it only runs - has some place to be I guess. This short tutorial will show you how to make your Pawn walk.

When the PlayerController is calculating its movement, it calls its HandleWalking function to check if the Pawn wants to walk. In its definition, you will notice a check against a variable bRun.

function HandleWalking()
{
if ( Pawn != None )
Pawn.SetWalking( bRun != 0 );
}


bRun is an input variable whose value is set when you hold down the Left Shift key. A search for bRun in your UTInput.ini will give you this.

Bindings=(Name="Walking",Command="Button bRun")

and a search for walking will give you this.

Bindings=(Name="LeftShift",Command="Walking")

If (bRun != 0) i.e. Pawn wants to walk, the Pawn's SetWalking funtion is called. Looking at this function's definition in UTPawn, you will notice that its blank and doesnt do anything. Assuming you have a subclass of UTPawn as your game's default pawn, you need to override the SetWalking function. You should end up with something similar to this.

class Mypawn extends UTPawn;

event SetWalking( bool bNewIsWalking )
{
super(Pawn).SetWalking(bNewIsWalking);
}

defaultproperties
{
}


Super refers to our parent class which in this case is UTPawn but by adding Pawn in parenthesis, we're actually calling the the SetWalking function in the Pawn class and not in UTPawn.

If you try this out, you will notice that what we have done is just make the character move slower but he still looks like he is running. To fix this, we have to play around with the AnimTree so fire up the editor. What we want to do is use a different animation when the pawn is walking. Walking reduces the pawn's speed/velocity.

Assuming you are using the default Anim Tree, add an UTAnimBlendBySpeed node like in the image below.



You will notice that I've set the minimum speed to 220 and the maximum speed to 440. By default, the UTPawn's maximum speed is 440 (GroundSpeed=440.0 in UTPawn.uc default properties) and its walking speed is half of this - so 220 (WalkingPct=+0.5 in Pawn.uc). So basically when the pawn is at full speed, the fast branch of the anim node is used entirelyand when walking the slow branch is used and when in the middle, the animations are blended together.

Thats it, enjoy walking.