Wednesday, June 27, 2012

Platformer Kit: SPG_AIPawn class (UnrealScript)

The SPG_AIPawn class represents the bots that attack the player in the Platform Kit. The variables defined for this class are:
class SPG_AIPawn extends Pawn
      placeable;

// Dynamic light environment component to help speed up lighting calculations for the pawn
var(Pawn) const DynamicLightEnvironmentComponent LightEnvironment;

// Ground speed of the pawn, display as Ground Speed in the editor
var(Pawn) const float UserGroundSpeed<DisplayName=Ground Speed>;

// Explosion particle system to play when blowing up
var(Pawn) const ParticleSystem ExplosionParticleTemplate;

// Explosion sound to play
var(Pawn) const SoundCue ExplosionSoundCue;

// Explosion damage amount 
var(Pawn) const int ExplosionDamage;

// Archetyped pick up to drop
var(Pawn) const archetype DroppedPickup ArchetypedPickup;

// Percentage chance to drop pickups
var(Pawn) const float ChanceToDropPickup;

// Current enemy
var Actor Enemy;

There is an Archetype for the SPG_AIPawn class. The image below shows the specific variables of the SPG_AIPawn class.


The Tick() function is executed every frame. In this function a reference to the player is stored in the Enemy variable, if this has not been done.

After that, if the bot is already walking on the ground (Physics == PHYS_Walking), it gets the player's location and adjust its velocity and rotation to move toward the player.
simulated function Tick(float DeltaTime)
{
    local PlayerController PlayerController;
    local Vector Direction;
    local Rotator NewRotation;

    Super.Tick(DeltaTime);

    // If we don't have an enemy yet...
    if (Enemy == None)
    {
        // Find the player controller in the world
        PlayerController = GetALocalPlayerController();

        if (PlayerController != None && PlayerController.Pawn != None)
        {
            // Set the enemy to the player controller's pawn
            Enemy = PlayerController.Pawn;
        }
    }
    else if (Physics == PHYS_Walking)
    {
        // Find the direction in order for me to look at my enemy
        Direction = Enemy.Location - Location;

        // Only need to use the yaw from the direction
        NewRotation = Rotator(Direction);
        NewRotation.Pitch = 0;
        NewRotation.Roll = 0;

        // Set the rotation so that I look at the enemy
        SetRotation(NewRotation);

        // Set my velocity, so I move towards the enemy
        Velocity = Normal(Enemy.Location - Location) * UserGroundSpeed;

        // Set my acceleration, so I move towards the enemy
        Acceleration = Velocity;
    }
}


When a bot collides with the player, the Bump() function is executed. For more information about collision read the article: "Collision in UnrealScript".

The player, who is represented by the variable Other in this function, receives damage whose value is set in the ExplosionDamage variable. In the image of the Archetype we can see that the default value of this variable is 5.

The particles effect that represents the explosion is triggered and is played the sound of the explosion stored in the variable ExplosionSoundCue. For more information about SoundCue, read the article: "Playing Sounds in UnrealScript".
event Bump(Actor Other, PrimitiveComponent OtherComp, Vector HitNormal)
{
    Super.Bump(Other, OtherComp, HitNormal);

    if (SPG_PlayerPawn(Other) != None)
    {
        // Apply damage to the bumped pawn
        Other.TakeDamage(ExplosionDamage, None, Location, Vect(0, 0, 0), class'DamageType');

        // Play the particle effect
        if (ExplosionParticleTemplate != None)
        {
            WorldInfo.MyEmitterPool.SpawnEmitter(ExplosionParticleTemplate, Location);
        }

        // Play the explosion sound
        if (ExplosionSoundCue != None)
        {
            PlaySound(ExplosionSoundCue);
        }

        // Destroy the pawn
        Destroy();
    }
}


When a bot is eliminated by the player, the PlayDying() function is executed. In this function the physics of the bot is defined as PHYS_RigidBody so that it falls to the ground.

Is checked the possibility of the bot drop a health item, which is represented by the class SPG_HealthPickup. This test is done with the expression: "FRand() <= ChanceToDropPickup". FRand() is an UnrealScript function that returns a random value between 0.0 and 1.0. The variable ChanceToDropPickup is defined in the Archetype with the value "0.3". This means that there is a chance of 30% that a health item falls from the bot.
simulated function PlayDying(class<DamageType> DamageType, vector HitLoc)
{
    local DroppedPickup DroppedPickup;

    Mesh.MinDistFactorForKinematicUpdate = 0.0;
    Mesh.ForceSkelUpdate();
    Mesh.SetTickGroup(TG_PostAsyncWork);
    CollisionComponent = Mesh;
    CylinderComponent.SetActorCollision(false, false);
    Mesh.SetActorCollision(true, false);
    Mesh.SetTraceBlocking(true, true);
    SetPawnRBChannels(true);

    SetPhysics(PHYS_RigidBody);

    Mesh.PhysicsWeight = 1.f;

    if (Mesh.bNotUpdatingKinematicDueToDistance)
    {
        Mesh.UpdateRBBonesFromSpaceBases(true, true);
    }

    Mesh.PhysicsAssetInstance.SetAllBodiesFixed(false);
    Mesh.bUpdateKinematicBonesFromAnimation = false;
    Mesh.WakeRigidBody();

    // Set the actor to automatically destroy in ten seconds.
    LifeSpan = 10.f;

    // Chance to drop a pick up
    if (ArchetypedPickup != None && FRand() <= ChanceToDropPickup)
    {
        // Spawn a dropped pickup
        DroppedPickup = Spawn(ArchetypedPickup.Class,,, Location,, ArchetypedPickup);

        if (DroppedPickup != None)
        {
            // Set the dropped pick up to falling
            DroppedPickup.SetPhysics(PHYS_Falling);

            // Set the velocity of the dropped pickup to the toss velocity
            DroppedPickup.Velocity.X = 0;
            DroppedPickup.Velocity.Y = 0;
            DroppedPickup.Velocity.Z = RandRange(200.f, 250.f);
        }
    }
}