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.
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.
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.


