TGB/PlatformerStarterKit/Basic Enemy Tutorial

From TDN

Contents

Adding a Basic Enemy to Your Platformer

This tutorial will go through the basic steps to get a custom enemy into the Platformer Starter Kit. All of the information here is available elsewhere on this site, but I thought a step by step tutorial might help some people. So, here we go....

Step 1: Set up the graphics

First, open or create your project using the Platformer template in TGB. Save the following image to your hard drive.

Now, in TGB, add a new ImageMap and import the Snake.png image. Double-click the new image map. Name the image map SnakeImageMap. Select CELL for the Image Mode and set the Cell Width and Cell Height to 128 x 128.

Next, we set up the animations. For the enemy we're creating, the engine would be happy with only a Fall animation, but we'll add a Run animation and a Die animation as well.

Fall Animation
Create a new animation, and choose the SnakeImageMap and click Select. Name it SnakeIdleAnimation. Add the third frame of the snake image map to the animation builder and click Save.

Run Animation
The Fall animation isn't very interesting, but we want the snake to slither across the platforms, so let's create a Run animation. Create a new animation, and choose the SnakeImageMap and click Select. Name it SnakeRunAnimation. Add the frames of the snake image map to the animation builder in the following order: 1, 2, 3, 2. Change the frame rate to 8fps, make sure the Cycle Animation box is checked and click Save.

Die Animation
Finally, we want the snake to disappear in style (well, style as far as programmer art is concerned) when we kill him, so let's create a Run animation. Create a new animation, and choose the SnakeImageMap and click Select. Name it SnakeDieAnimation. Add the frames of the snake image map to the animation builder in the following order: 4,5,6,7,8,9. Change the frame rate to 8fps, make sure the Cycle Animation box is NOT checked and click Save.

Step 2: Add a datablock for our snake

Now that the graphics are created, we need to tell TGB and the PSK about our snake. We do this by creating a datablock. We will edit the datablocks.cs file found at YourGameDirectory/game/gameScripts/datablocks.cs Add the following block of code to the file. (I added mine right before the DrillActorTemplate datablock (or about line 67 if you're working with an unmodified copy of the latest version of the PSK (1.2 for TGB 1.7.5)

datablock t2dSceneObjectDatablock( SnakeActorTemplate )
{
    Class             = "SnakeClass";
    ActorType         = "Snake";
    
    Size              = "12.000 12.000";
    Layer             = "2";
    CollisionPolyList = "-0.300 -0.250 0.300 -0.250 0.400 0.250 0.000 0.480 -0.300 0.400";
    _Behavior0        = "AIControllerBehavior\tAIType\tDrill";
    MaxMoveSpeed      = 16;
    GroundAccel       = 1000;
    GroundDecel       = 1000;
    
    AllowRespawn      = false;
    DeathTimeOut      = 500;
};

Step 3: Handling Snake/Player Collisions - Let the Killing Begin!!!

Right now, we can add a Snake to our game and it would slither along, turning around when it hits an Enemy Turn Trigger, but it doesn't hurt the player, and the player can't kill it. Where's the fun in that? So, let's add a function to handle the collision. Open the file PlayerMethods.cs (YourGame/game/gameScripts/PlayerMethods.cs). Find the function called resolveDrillCollision. Right after that function, add the following function:

function PlayerClass::resolveSnakeCollision( %ourObject, %theirObject, %normal )
{
    // Get the contact angle
    %angle = mRadToDeg( mAtan( -%normal.X, %normal.Y ) );
    %angle = mAbs( %angle ) % 360;

    // Check if we've hit the drill properly
    if ( (%angle <= 40 || %angle >= 360 - 40 )
        && %ourObject.LinearVelocity.Y > %theirObject.LinearVelocity.Y
        && %ourObject.Position.Y < %theirObject.Position.Y )
    {
        // Do damage to the drill and bounce the player
        %theirObject.takeDamage( %theirObject.Health, %ourObject, true, true );

        // Stop them from moving
        %theirObject.Direction = "0.0 0.0";
        %theirObject.Gravity   = "0.0 0.0";

        %ourObject.bounce( %ourObject.JumpForce / 2, 180 );
    }
    else
    {
        // Do damage to the player
        %ourObject.takeDamage( 10, %theirObject, true, false );
        if ( %ourObject.isMethod( "onAreaDamage" ) )
        {
            %ourObject.onAreaDamage( %ourObject, %normal );
        }
    }
}

Basically, this function is a copy of the Drill collision function, only it handles damage and the player's damage Response directly (whereas the drill handles that in its mounted DrillHead object.) Our snake is squishable. You can kill him by landing on top of him, but if he runs into you, he hurts you.

Next, we need to call our new resolveSnakeCollision function when the player collides with a snake. In PlayerMethods.cs, find the following code:

/// Called when a player collides with an enemy.
function PlayerClass::resolveEnemyCollision( %ourObject, %theirObject, %normal )
{
    // Resolve the collisions differently for different ai types
    switch$ ( %theirObject.ActorType )
    {
        case "Drill" : %ourObject.resolveDrillCollision( %theirObject, %normal );
    }
}

and replace it with the following code:

/// Called when a player collides with an enemy.
function PlayerClass::resolveEnemyCollision( %ourObject, %theirObject, %normal )
{
    // Resolve the collisions differently for different ai types
    switch$ ( %theirObject.ActorType )
    {
        case "Drill" : %ourObject.resolveDrillCollision( %theirObject, %normal ); break;
        case "Snake" : %ourObject.resolveSnakeCollision( %theirObject, %normal );
    }
}

Step 4: Add the snake to our Level

Now, all that's left to do is to add the snake to our level. Drag an animated sprite (SnakeRunAnimation works great) onto the level where we want our snake to spawn. Add the Spawn Point Behavior. Select pskActor2d as the type and SnakeActorTemplate as the target object (you will need to reload the project if the SnakeActorTemplate is not in the drop-down list.) Add triggers to create boundaries for the snake if you desire. Otherwise, hit play and go kill some snakes.

 

Other Notes

This tutorial was written by Joseph Ellis. The snake graphic was drawn by me as well. While art is not my strong suit, you are welcome to use the snake graphic should you desire. Crediting me is optional.

Another Note

Make sure your enemy at the minimal has a Spawn, Fall, and Idle animation or the game will crash, even if it's just a single frame place holder. Spawn and Fall do not need to loop, Idle should loop. Also check your spelling, nothing worse then a game crashing because it's called SnakeSpawnAniimation for example.

Giving the enemy its own parameters

Up until now the parameters of the enemy are based upon a collision, but if you want your enemy to have more depth then it's going to need its own methods so you can specify things better.

Within the same folder as PlayerMethods.cs create a new file named SnakeMethods.cs and enter the following base code inside:

function SnakeClass::onAddToScene( %this )
{

}

function SnakeClass::onRemove( %this )
{

}

function SnakeClass::onRespawn( %this, %dAmount, %srcObject )
{
    %this.DisableGravity = false;
}

function SnakeClass::onLanded( %this, %platformObject )
{

}

function SnakeClass::onDeath( %this, %dAmount, %srcObject )
{

}

function SnakeClass::onWallCollision( %this, %wall, %normal )
{
    %this.Direction.X = %normal.X;
}

Remember to edit GameMethods.cs and add the following line so your new script is recognized by the engine:

    exec ( "./SnakeMethods.cs" );

Now, you can have specific parameters for the snake. Anything that you may have done within PlayerMethods.cs can probably be translated to SnakeMethods.cs smoothly.