Robot Foot Games Helping you improve your games as we improve our own. :)

30May/112

Adding melee to PSK

Update: Went through the entire tutorial in a fresh solution and fixed one bug where the enemies no longer killed you. The tutorial has been updated (UpdateEnemies(...) method in Level.cs) and now works as-is. If you have problems, make sure to download the file just below and compare it with yours to see where you may have messed up!

You can download a working version below.

 

Here's a video showing off the final product of this tutorial:

Here is what I used for his attack animation as a placeholder, feel free to use it. Place this in the same place as the other animations: “HighResolutionContent>Sprites>Player”. Also make sure to add it to your project: Right click on the Player folder and Add>Existing Item>Attack.png

 

 


 

Player.cs

Find the following:

private float jumpTime;

After it add the following:

// Attacking state
public bool isAttacking;
const float MaxAttackTime = 0.33f;
public float AttackTime;

 

Next find the following:

public Rectangle BoundingRectangle
{
    get
    {
        int left = (int)Math.Round(Position.X - sprite.Origin.X) + localBounds.X;
        int top = (int)Math.Round(Position.Y - sprite.Origin.Y) + localBounds.Y;
        return new Rectangle(left, top, localBounds.Width, localBounds.Height);
    }
}

After that add the following:

public Rectangle MeleeRectangle
{
    get
    {
        int left = (int)Math.Round(Position.X - sprite.Origin.X) + localBounds.X;
        int top = (int)Math.Round(Position.Y - sprite.Origin.Y) + localBounds.Y;
        if (flip == SpriteEffects.FlipHorizontally)
            return new Rectangle(
                (left + localBounds.Width),
                top,
                localBounds.Width,
                localBounds.Height);
        else
            return new Rectangle(
                (left - localBounds.Width),
                top,
                localBounds.Width,
                localBounds.Height);
    }
}

 

At the top of Player.cs find the following line:

private Animation dieAnimation;

After that add the following line:

private Animation attackAnimation;

 

Now find the LoadContent method in Player.cs and find the following line:

dieAnimation = new Animation(
    Level.Content.Load<Texture2D>("Sprites/Player/Die"),
    0.1f,
    false);

After that add the following:

attackAnimation = new Animation(
    Level.Content.Load<Texture2D>("Sprites/Player/Attack"),
    0.1f,
    false);

 

Find the DoJump method and add a new method after it:

private void DoAttack(GameTime gameTime)
{
    // If the player wants to attack
    if (isAttacking)
    {
        // Begin or continue an attack
        if (AttackTime > 0.0f)
        {
            AttackTime -= (float)gameTime.ElapsedGameTime.TotalSeconds;
        }
        else
        {
            isAttacking = false;
        }
    }
    else
    {
        //Continues not attack or cancels an attack in progress
        AttackTime = 0.0f;
    }
}

 

Now our attack is setup but we need a keybind so we can actually use it! At the very top of our player class add just the KeyboardState line below:

class Player
{
    KeyboardState previousKeyboardState = Keyboard.GetState();

Now add the following in the GetInput method:

if (previousKeyboardState.IsKeyUp(Keys.F) && keyboardState.IsKeyDown(Keys.F))
{
    if (AttackTime != MaxAttackTime)
    {
        isAttacking = true;
        AttackTime = MaxAttackTime;
    }
}

 

Now onto the Update method, after the GetInput() call add the following:

DoAttack(gameTime);

 

Find the following in the Update method:

if (IsAlive && IsOnGround)
{
    if (Math.Abs(Velocity.X) - 0.02f > 0)
    {
        sprite.PlayAnimation(runAnimation);
    }
    else
    {
        sprite.PlayAnimation(idleAnimation);
    }
}

And change it to look like the following:

if (IsAlive && IsOnGround)
{
    if (isAttacking)
    {
        sprite.PlayAnimation(attackAnimation);
    }
    else
    {
        if (Math.Abs(Velocity.X) - 0.02f > 0)
        {
            sprite.PlayAnimation(runAnimation);
        }
        else
        {
            sprite.PlayAnimation(idleAnimation);
        }
    }
}

 

BUILD THE SOLUTION AT THIS POINT TO MAKE SURE YOU DON’T HAVE ANY ERRORS.

 

Enemy.cs

In Enemy.cs find the following:

// Animations
private Animation runAnimation;
private Animation idleAnimation;
private AnimationPlayer sprite;

and add the following after it:

private Animation dieAnimation;
// Sounds
private SoundEffect killedSound;
public bool IsAlive { get; private set; }
private const float deathTimeMax = 1.0f;
public float deathTime = deathTimeMax;

 

Here we added a death animation, death sound, and an IsAlive variable that will allow our enemies to die. We also added a timer so we can make our enemy disappear a certain amount of time after he dies. If you want to change how long they're alive simply change the deathTimerMax variable.

To initialize the IsAlive variable find the constructor for our Enemy, and after,

this.position = position;

add:

this.IsAlive = true;

 

First we need to load the sound into the project because it isn’t there by default. In the solution explorer go to “SharedContent” right click on Sounds>Add>Existing Item and add “MonsterKilled”. Right click on MonsterKilled and go to Properties. In the Properties window find ContentProcessor and from the drop down menu to the right of that choose Sound Effect – XNA Framework. At the very top add another using statement:

using Microsoft.Xna.Framework.Audio;

Once you add the sound, you'll need to change it's properties because it's considered a 'Song' instead of a 'Sound Effect'. Right click on the sound after you add it to your solution and hit properties. Under 'Content Processor' change it from Song to Sound Effect. We need to load the new sound so find the LoadContent method and add the following at the bottom:

//Load sounds.
killedSound = Level.Content.Load<SoundEffect>("Sounds/MonsterKilled");

While we’re in the LoadContent method add the following by the other animations: (NOTE: You also need to add this animation to your solution because it isn't there by default. Navigate to HighResContent>Sprites and then in each folder (MonsterA, MonsterB, etc) you'll have to add the existing item like you did above with the sound)

dieAnimation = new Animation(Level.Content.Load<Texture2D>(spriteSet + "Die"), 0.07f, false);

 

Inside the Update method add this block of code.

if (!IsAlive)
    deathTime -= (float)gameTime.ElapsedGameTime.TotalSeconds;

 

Inside of the Draw method let's change the code a little bit. Change the if/else statement to look like the following:

// play death animation if we're dead, stop running
//when the game is paused or before turning around.
if (deathTime < deathTimeMax)
    sprite.PlayAnimation(dieAnimation);
else if (!Level.Player.IsAlive ||
    Level.ReachedExit ||
    Level.TimeRemaining == TimeSpan.Zero ||
    waitTime > 0)
{
    sprite.PlayAnimation(idleAnimation);
}
else
{
    sprite.PlayAnimation(runAnimation);
}

 

Under the Draw method add a new method:

public void OnKilled(Player killedBy)
{
    IsAlive = false;
    killedSound.Play();
}

 

Level.cs

Next in Level.cs find the following in the UpdateEnemies method (and add the bold part)

// Touching an enemy instantly kills the player
if (enemy.BoundingRectangle.Intersects(Player.BoundingRectangle))
{
    if(enemy.IsAlive)
        OnPlayerKilled(enemy);
}

Now add this if statement after the one above:

if (enemy.IsAlive && enemy.BoundingRectangle.Intersects(Player.MeleeRectangle))
{
    if(Player.isAttacking)
        OnEnemyKilled(enemy, Player);
}

 

Now under the UpdateEnemies method add a new method:

private void OnEnemyKilled(Enemy enemy, Player killedBy)
{
    enemy.OnKilled(killedBy);
}

 

Now in the Draw(...) method find the block of code to draw the enemies and change it to this:

foreach (Enemy enemy in enemies)
    if(enemy.IsAlive || enemy.deathTime > 0)
        enemy.Draw(gameTime, spriteBatch);

 

And that's it! Give it a try and see if you can finally kill those bad guys!

Comments (2) Trackbacks (0)
  1. Hi, I wonder how would you do that in Windows Phone with touch countrols?

    • It depends on the game. You could do touch screen controls so you could jump/melee/move with the on-screen controls. You could also move with the accelerometer and tap on enemies to attack them. The XNA website has examples for both so you can toy around with those to see which idea you like better.


Leave a comment

(required)

No trackbacks yet.