The Legend of the Tomato Quest Tutorial - Moving the Player

From SwinGame

This tutorial focuses on Moving the Player around the game, and getting him to collide with the Level.

This page contains a Tutorial. Tutorials are designed to walk you through the development of a small game.

Contents

Moving the Character

Now we need to make a routine that moves the player. Add the following method to the Character class in Character.cs

public void MoveCharacter(Map theMap, int moveX, int moveY)
{
    //Create a Vector that represents the Sprites new movement
    Vector tempVector = Physics.CreateVector((float)moveX, (float)moveY);
    _Sprite.Movement.SetTo(tempVector);
 
    //Move Sprite to new Location
    Graphics.MoveSprite(_Sprite, _Sprite.Movement);
 
    //If Colliding with map, undo the movement.
    if (MappyLoader.CollisionWithMap(theMap, _Sprite) != CollisionSide.None)
    {
        Graphics.MoveSprite(_Sprite, Physics.InvertVector(_Sprite.Movement));
    }
}

This method is used to move our player. This method can be re-used for our AI rather then just limit this method to the player, since AI will be Characters as well. The first parameter is the map, this is required to deal with collisions between the Character that is passed in and the map.

The last 2 parameters are the moveX and moveY parameters, which are simply tell this method how far we want to move our character on the X axis and Y axis.

The first thing that this method does is, it creates a new Vector, that represents the movement. The reason we didn't make this method pass in a vector is because it simplifies its use when moving in other directions. For example, moving Up could take 0,-1 while moving down would be 0,1.

Once the vector is created, we assign this new vector to the character movement, and then we move based off its movement using the MoveSprite command.

The final part of the code, checks whether the character has collided with the map since the last movement. If the character is not colliding, then nothing happens, but if the character is colliding, then the movement of the character is inverted, and the character is moved again, so that it is no longer colliding with the map.

Character Animation

The next part can be a bit tricky. Animating the character isn't as simple as it may appear, due to the fact that we only want to show certain frames, based on the direction the character is moving.

Image:HeroFrames.png

Here we can see that, the Upward movement frames go from 0 to 2 (remember that the index starts at 0), the Right movement frames go from 3 to 5, the Down movement frames go from 6 to 8 and finally, the Left frames go from 9 to 11.

To make sure that only the correct frames are shown when we move, we need a way of determining which way the character is going.

Add the following enumeration to the top of the Character.cs file, outside of the Character class.

public enum CharacterAnim
{
    Top,
    Down,
    Left,
    Right,
    None
}

Here we have a value for each of the 4 directions, and a 5th value "None" for when the character is not moving.

We also need to add some code to the Character class. Modify the Character class in Character.cs to include the new field and accessor as shown below:

public class Character
{
    //Constant
    const Event PLAYERSPAWN = Event.Event1;
 
    private Sprite _Sprite;
 
    //ADD THIS FIELD
    private CharacterAnim _Anim;
 
    //Returns the Sprite
    public Sprite Sprite
    {
        get { return _Sprite; }
    }
 
    //ADD THIS ACCESSOR
    //Returns the Animation
    public CharacterAnim Anim
    {
        get { return _Anim; }
    }

Now every character we make will have a CharacterAnim value. But we also need to set some more values for the character, for this we need to modify the Character constructor.

Modify the Character constructor in the Character class in Character.cs method to look like this.

//Character Constructor
public Character(String name, int SpawnX, int SpawnY)
{
    //Load the Character Sprite
    _Sprite = Graphics.CreateSprite(Resources.GameImage(name), 3, 12, 24, 32);
 
    //Position the Character
    _Sprite.xPos = SpawnX;
    _Sprite.yPos = SpawnY;
 
    //ADD THESE 3 LINES
    _Sprite.EndingAction = SpriteEndingAction.ReverseLoop;
    _Sprite.Movement.SetTo(Physics.CreateVector(0, 0));
    _Anim = CharacterAnim.None;
}

The first line that has been added, changes the Sprites ending action to reverse loop. What this will do is when our character is animating, he'll put one foot out, and then bring that foot back in, and then move the other foot out, and bring that foot back in.

By default animations play in a loop, when the animation reaches the end, it goes straight back to the start of the animation and plays again. Changing this value to ReverseLoop will make the animation go forwards, then backwards and forwards again, over and over.

The next line sets the Character's sprite movement to 0,0, so that the character is not moving. And finally the last line sets the CharacterAnim value of the character to None (since he isn't moving yet).

Now the next thing to do is to create a function that sets the delay for each frame in the animation, so that only the frames we want are displayed. The frames that we do not want to show will be given a value of 0, which essentially means they are skipped over, which is good.

Add the following method to the Character class in Character.cs file.

private void SetAnimationFrames(int startingFrame, int startingIndex, int endingIndex)
{
    //Create a new temporary Array, the size of the Frame Count
    int[] tempintarr = new int[_Sprite.FrameCount];
 
    //Set all elements to 0
    for (int i = 0; i < tempintarr.Length; i++)
    {
        tempintarr[i] = 0;
    }
 
    //Sets the Current Frame
    _Sprite.CurrentFrame = startingFrame;
 
    //Set the new Frame Values
    for (int i = startingIndex; i < endingIndex + 1; i++)
    {
        tempintarr[i] = 7;
    }
 
    //Set the Frames per cell to the new array
    _Sprite.FramesPerCell = tempintarr;
}

This method is very simple, the first parameter is the starting Frame, the frame that we want our animation to first start on when we use this method.

For example, if we change the animation to the Upward animation (Frames 0, 1 and 2) we want our animation to start on Frame 1, because in this frame, the character is standing perfectly upright.

The second and third parameter, tells the method the starting and ending index of the new animation we want. For example, in the Upward movement, we want Frames 0 to 2, so these parameters for the Upward movement would also be 0 and 2.

The first thing that is done inside the method, is a new array of Integer's created with the same size as the Sprite's animation frame count. Next we iterate through this new array setting all of its values to 0, which when assigned to the animation frames, will cause them to skip. Next the current frame it set to the starting frame passed into the method.

Next we iterate through the part of the array that we actually want to see, by starting and ending with the index's passed into method. We set the delay for each of these frames to 7.


We have one final method to add, which will use the above method to alter the animation of the Sprite according to the direction of the player.

Add the following method to the Character class in Character.cs

public void UpdateCharacterAnimation()
{
    //If the Sprite Movement is going Up, set the animation Up
    if (_Sprite.Movement.Y < 0)
    {
        if (_Anim != CharacterAnim.Top)
        {
            _Anim = CharacterAnim.Top;
            SetAnimationFrames(1, 0, 2);
        }
        Graphics.UpdateSpriteAnimation(_Sprite);
    }
 
    //If the Sprite Movement is going Down, set the animation Down
    if (_Sprite.Movement.Y > 0)
    {
        if (_Anim != CharacterAnim.Down)
        {
            _Anim = CharacterAnim.Down;
            SetAnimationFrames(7, 6, 8);
        }
        Graphics.UpdateSpriteAnimation(_Sprite);
    }
 
    //If the Sprite Movement is going Left, set the animation Left
    if (_Sprite.Movement.X < 0)
    {
        if (_Anim != CharacterAnim.Left)
        {
            _Anim = CharacterAnim.Left;
            SetAnimationFrames(10, 9, 11);
        }
        Graphics.UpdateSpriteAnimation(_Sprite);
    }
 
    //If the Sprite Movement is going Right, set the animation Right
    if (_Sprite.Movement.X > 0)
    {
        if (_Anim != CharacterAnim.Right)
        {
            _Anim = CharacterAnim.Right;
            SetAnimationFrames(4, 3, 5);
        }
        Graphics.UpdateSpriteAnimation(_Sprite);
    }
}

This method is fairly big, but all its doing is checking which way the character is moving, and if it wasn't moving in that direction before, changes the Anim value of the Character to that specific direction. It also sets the frames appropriately for each direction.

At the end of each section the Animation is updated to show the new animation.

Adding the Controls

Before we add the controls we need to add some code to the Level.cs source code.

Add the following accessor to the Level class in Level.cs

//Returns the Map
public Map Map
{
    get { return _Map; }
}

This code will return the actual map from the level, so we can use it for collisions.

The next step is to write the code that assigns each of the movement's to a key.

Create a new source file, called Controller.cs and add the following code to that file.

using System;
using System.Drawing;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Text;
 
using SwinGame;
using Graphics = SwinGame.Graphics;
using Bitmap = SwinGame.Bitmap;
using Font = SwinGame.Font;
using FontStyle = SwinGame.FontStyle;
using Event = SwinGame.Event;
using CollisionSide = SwinGame.CollisionSide;
using Sprite = SwinGame.Sprite;
using Keys = SwinGame.Keys;
 
using GameResources;
 
namespace TomatoQuest
{
    public class Controller
    {
        //Update Input Routine
        public void UpdateInput(Character thePlayer, Level theLevel)     
        {
            //If Up key is hit, move the character up
            if (Input.IsKeyPressed(SwinGame.Keys.VK_UP))
            {
                thePlayer.MoveCharacter(theLevel.Map, 0, -2);
            }
            //If Down key is hit, Move the character down
            else if (Input.IsKeyPressed(SwinGame.Keys.VK_DOWN))
            {
                thePlayer.MoveCharacter(theLevel.Map, 0, 2);
            }         
            //If Left key is hit, move the character left
            else if (Input.IsKeyPressed(SwinGame.Keys.VK_LEFT))
            {
                thePlayer.MoveCharacter(theLevel.Map, -2, 0);
            }
            //If Right key is hit, move the character right
            else if (Input.IsKeyPressed(SwinGame.Keys.VK_RIGHT))
            {
                thePlayer.MoveCharacter(theLevel.Map, 2, 0);
            }
            //If no directional key is hit, don't move him
            else
            {
                thePlayer.MoveCharacter(theLevel.Map, 0, 0);
            }
 
            //Update Character's Animation
            thePlayer.UpdateCharacterAnimation();
    
        }
    }
}

This new Controller Class, now has a new UpdateInput method, which takes in a Character and a Level. It then checks each of the keys, UP, DOWN, LEFT and RIGHT, and moves the character in that direction. Finally it updates the animation of the Character.

With this code, the character is limited to moving in a single direction at a time, to keep things simple.

Finishing Up

The last thing we need to do is add some code to the game loop, so that our player can move.

Add the following field to the Game class in Game.cs.

public class Game
{
    //Constant
    const Event PLAYERSPAWN = Event.Event1;
 
    //Loads the Map at object creation
    private Level _Map = new Level("Level1");
 
    //Load the Player
    private Character _Player;
 
    //ADD THIS FIELD
    //Load the Player Controller
    private Controller _Controller = new Controller();
 
    ...

Also add the following line of code to the Game class in the Run() method.

public void Run()
{
    //Draw the Map
    _Map.DrawLevel();
 
    //ADD THIS LINE OF CODE
    //Update Player with Controls
    _Controller.UpdateInput(_Player, _Map);
 
    //Draw Player
    _Player.DrawCharacter();
 
    //Make the Camera follow the Player
    Camera.FollowSprite(_Player.Sprite, 0, 0);
 
    //Draw the FrameRate
    Text.DrawFramerate(200, 0, Resources.GameFont("Courier"));  
}

Now we can build the project and play around with our new moving character.

The Project so far

Here's a zip containing the source and resources of the tutorials we've completed up to this point. You should find this useful to read through as you read this tutorial.

Source Code

Tutorial Table of Contents

  1. The Legend of the Tomato Quest Tutorial
  2. Loading the level
  3. The Player
    1. Loading and Drawing the Player
    2. Moving the Player
  4. Player Stats
  5. User Interface
  6. Attacking Animation
  7. AI
    1. The Healer
    2. The Healer II
    3. Enemies
    4. Enemies II
  8. Interaction
  9. Combat
  10. Items and End Game Conditions
  11. Additions and Improvements