Posts tagged ‘Platformer’

Adding Triggers to the Platformer Starter Kit

I’ve seen a lot of posts where people are using the Platformer Starter Kit and as I was reading one, I thought having the ability to have triggers in a level might be something someone would find useful.

Triggers give the level designer the ability to make levels more challenging and diverse, instead of simply having the player run around collecting gems and power-ups in a wide open level. The added dimension of a goal that may have to be reached before exiting the level makes the game more challenging, which leads to the game possibly being more interesting to play.

The first use that I thought of for a trigger was to remove a block( s ) that impedes reaching the exit. Should be simple enough, right? Let’s find out.

I started out by figuring that two kinds of triggers might be good to have – visible and invisible. Even though triggers aren’t tiles, the level designer needs to see them in the level data, which means the code that reads the text files that hold the level data have to account for them. “T” and “I” are what I selected for the two trigger types.

In the Level.cs file’s LoadTile method, add the following to the end of the switch statement before the “default” case:

// visible trigger
case 'T':
   return new Tile(null, TileCollision.Passable);

//invisible trigger
case 'I':
   return new Tile(null, TileCollision.Passable);

 

For the visible trigger, the player will have to have something to see, which means we’ll need a sprite. Behold, the awesome programmer art!

trigger

I placed the file in the Sprites folder in the Content project.

The sprite doesn’t look exactly like this (at least not as I’m typing this). You can open the file in the attached source linked to at the bottom of this post to see it. The important thing is to ensure that whatever you use is sized correctly (I just opened the gem.png file and had at it) and has a transparent background.

The sprite needs an object to load it into and we’ll need something to hold one or more triggers in the level, so add the following at the top of the Level.cs file’s Level class:

private List<Trigger> triggers = new List<Trigger>();
private Texture2D triggerTexture;

 

The Trigger class looks like this:

public enum TileTriggerType
{
    None,
    MoveTile,
    RemoveTile
}

public class Trigger
{
    public Vector2 Location;
    public bool IsTriggerVisible = true;
    public Vector2 TriggerTarget = Vector2.Zero;
    public TileTriggerType TriggerType = TileTriggerType.None;
    public bool IsActive = true;
}

Since the text file that holds the level data is fairly set in stone as far as it’s content, we’ll use separate files to hold the data for a trigger. I decided on something fairly straightforward as far as format:

69,13|2|70,0|1

The pipe character separates the pieces of data. The first piece is the location of the trigger, the second the type of trigger, the third, the location of the tile that the trigger acts upon, the last whether or not the trigger is visible.

The following code reads the trigger file for the current level:

//load level triggers data
try 
{            
    using (Stream stream = TitleContainer.OpenStream("Content/triggers/" + levelIndex.ToString() + ".txt"))
    {
        using (StreamReader sr = new StreamReader(stream))
        {
            string line = sr.ReadLine();

            while (line != null)
            {
                Trigger trigger = new Trigger();

                string[] data = line.Split('|');

                Vector2 vec = new Vector2(Convert.ToInt16(data[0].Substring(0, data[0].IndexOf(','))), Convert.ToInt16(data[0].Substring(data[0].IndexOf(',') + 1)));
                trigger.Location = vec;
                trigger.TriggerType = (TileTriggerType)Convert.ToInt16(data[1]);
                vec = new Vector2(Convert.ToInt16(data[2].Substring(0, data[2].IndexOf(','))), Convert.ToInt16(data[2].Substring(data[2].IndexOf(',') + 1)));
                trigger.TriggerTarget = vec;
                trigger.IsTriggerVisible = data[3] == "1" ? true : false;

                triggers.Add(trigger);

                line = sr.ReadLine();
            }
        }
    }
}
catch (Exception ex)
{
    //do nothing
        
}

triggerTexture = Content.Load<Texture2D>("Sprites/trigger");

 

The last piece of the puzzle is to handle the player hitting the trigger. Add the following to the Update method of the Level class after the UpdateEnemies(gameTime) line:

//check for hitting trigger
if (Player.IsAlive && Player.IsOnGround)
{
    foreach(Trigger trigger in triggers)
    {
        if(trigger.IsActive)
        {
            //offset to center of trigger texture
            if (Player.BoundingRectangle.Contains((int)trigger.Location.X * Tile.Width + Tile.Width / 2, (int)trigger.Location.Y * Tile.Height + Tile.Height / 2))
            {
                trigger.IsActive = false;
                switch (trigger.TriggerType)
                {
                    case TileTriggerType.MoveTile:
                    {


                        break;
                    }
                    case TileTriggerType.RemoveTile:
                    {
                        tiles[(int)trigger.TriggerTarget.X, (int)trigger.TriggerTarget.Y] = new Tile(null, TileCollision.Passable);

                        break;
                    }
                }
            }
        }
    }
}

 

I’ve only handled the RemoveTile trigger type. I’m leaving the MoveTile as an exercise for the reader (that’s my story and I’m sticking to it!).

I modified the first level to add a wall blocking the exit and set the trigger near it so the player can see the tile being removed:

......................................................................#.......
......................................................................#.......
...........................G..........................................#.......
..........................###.........................................#.......
......................G...............................................#....X..
.....................###................G.GDG.............G.G.G...############
.................G....................#########..........#######..............
................###...........................................................
............P...................G.G...............G.G.........................
...........###.................#####.............#####........................
.......G......................................................................
......###...............................GDG.G.............G.G.G.G.G.G.G.G.G.G.
......................................#########.........##.G.G.G.G.G.G.G.G.G..
.1........................................................GCG.G.GTGCG.G.G.GCG.
####################......................................####################

Save everything and give it a try. Assuming I didn’t miss anything you should be able to hit the trigger and see the top block in the wall disappear, enabling you to reach the exit. If I did miss something please let me know. In that case you can download the source from here and give it a try.

Other possible triggers that I could see being useful:

  • Moving blocks to enable the player to reach areas not accessible otherwise
  • Moving blocks to crush enemies (this would be a pretty big modification)
  • Moving blocks to prevent enemies from reaching and area and allowing the player to safely move through that area
  • Moving blocks to allow access to power-ups
    One thing that currently isn’t handled by this code is the ability to have a trigger modify multiple tiles. This isn’t a big enhancement. Simply change the TriggerTarget member of the Trigger class to a List<Vector2>, change the code to read the trigger file to handle one or more pieces of data (possibly by making the target data the last piece of data read) and changing the code in the Level class Update method to loop through this List.
    There are a lot of areas of level design that are opened up by a pretty simple modification to the game code. I’d love to see what people can come up with.
    If anyone can think of other small enhancements to the kit, let me know.

Modifying Platformer Starter Kit–Adding Health

So there was another post on the AppHub forums where someone was asking about how to add health to the character in the Platformer Starter Kit rather than having him die immediately when touching an enemy. I’d cobbled together a bunch of mods that had previously been done and thought this might make another good one.

Implementing this turned out to be pretty simple. Just two files need updating, Level.cs and Player.cs and I added two sprites – a health bar and a 1×1 pixel texture to fill in the healthbar.

healthbar

health

(magnified may times Open-mouthed smile)

 

First, the changes to the Player.cs file. At the top where all the members are declared add:

private int health = 100;
//private int lives = 3;
private int lives = 1;
private bool touchingEnemy;

public bool TouchingEnemy
{
    get { return touchingEnemy; }
    set { touchingEnemy = value; }
}

public int Health
{
    get { return health; }
}

 

Notice, the old “lives” member has been commented out and replaced. The player now only has one life, but can be injured a number of times before dying (which we’ll see in a second).

Previously, when the player was touched by an enemy the OnKilled method was called. We need to add a new method (I put it right under the OnKilled method):

public void OnInjured()
{
    touchingEnemy = true;

    health -= 10;

    if (health == 0)
    {
        killedSound.Play();
        sprite.PlayAnimation(dieAnimation);
        isAlive = false;
    }
}

 

Now we’ll add the textures necessary to render the health bar and the members of the level class to load the textures into.

You can either save the textures above by right-clicking on them, create your own, or grab them out of the zip file attached to this post. Drop them into the “sprites” folder in the content project (or wherever you want, but you’ll have to change the relevant code that loads them).

Add the following code to the Level.cs file’s section where the other members are declared:

private Texture2D healthBar;
private Vector2 healthBarLoc;
private Texture2D healthTexture;

 

Then add the following to the Level.cs constructor:

//load healthbar textures
healthBar = Content.Load<Texture2D>("sprites/healthbar");
healthTexture = Content.Load<Texture2D>("sprites/health");
healthBarLoc = new Vector2(Width * Tile.Width - 205, 5);

 

The Width member of the Level class returns the width of the screen measure in number of tiles, so we have to do a little multiplication and offset from the edges of the screen a bit.

Now add the matching method to injure the player that we had in the Player class (again, I added this right below the OnKilled method):

private void OnPlayerInjured()
{
    Player.OnInjured();
}

 

And we’ll call it in the UpdateEnemies method:

private void UpdateEnemies(GameTime gameTime)
{
    foreach (Enemy enemy in enemies)
    {
        enemy.Update(gameTime);

        // Touching an enemy now just injures the player
        if (enemy.BoundingRectangle.Intersects(Player.BoundingRectangle))
        {
            if (!player.TouchingEnemy)
                //OnPlayerKilled(enemy);
                OnPlayerInjured();
        }
        else
            UpdatePlayerCollision(false);
    }
}

private void UpdatePlayerCollision(bool touchingEnemy)
{
    player.TouchingEnemy = touchingEnemy;
}

 

That should be it. Save everything and run the game. You should see the health bar change every time you touch an enemy. You’ll have to advance to the second level to see one. After ten touches the character should die and you’ll be presented with a dialog. I haven’t changed the dialog or relevant code to restart the game, nor did I remove the sprite rendering the number of lives the player has. I wanted to keep the latter in so that the option to add a life power-up can be done without breaking the game. The former is something for you to implement. Smile

Hopefully someone will find this useful.

 

Source code can be downloaded from here.

You’ve Got Your Gamestate Management Sample In My Platformer

I tweeted about this earlier, but the update is a bit too much for a tweet. I added a couple of versions to the merged Platformer/Gamestate Management sample that I did as a result of a question on the AppHub forums. Here are all the relevant links:

Bare bones merged version – link

+ lives – link

+ scrolling level – link

+ power-ups – link

+ moving tiles – link