0
votes

I am making and RPG and have coded the movement and animation for the sprites. When I run the project, the sprite will move down or right. When the right arrow key is pressed, the sprite will move right, but will not stop. When the down key is pressed, the sprite will move down, and stop when the key is released. However, when you press another arrow key once you have pressed the down or right key, the sprite image won't change direction. Also, the sprite images do not change when a new arrow key is pressed. Why is this happening? Any help is appreciated.

Game.cs

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace RPG
{
    /// <summary>
    /// Default Project Template
    /// </summary>
    public class Game1 : Game
    {

        #region Fields

        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Vector2 aPosition = new Vector2(250, 100);
        MageChar mageSprite;

        #endregion

        #region Initialization

        public Game1 ()
        {

            graphics = new GraphicsDeviceManager (this);

            Content.RootDirectory = "Content";

            graphics.IsFullScreen = false;
        }

        /// <summary>
        /// Overridden from the base Game.Initialize. Once the GraphicsDevice is setup,
        /// we'll use the viewport to initialize some values.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            mageSprite = new MageChar();
            base.Initialize();
        }

        /// <summary>
        /// Load your graphics content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // TODO: use this.Content to load your game content here
            mageSprite.LoadContent(this.Content);           
        }

        #endregion

        #region Update and Draw

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // game exits id back button is pressed
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            // TODO: Add your update logic here
            //calls update method of MageChar class
            mageSprite.Update(gameTime);

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself. 
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            graphics.GraphicsDevice.Clear(Color.CornflowerBlue);

            // TODO: Add your drawing code here
            spriteBatch.Begin();
            //calls draw method of MageChar class
            mageSprite.Draw(this.spriteBatch);
            spriteBatch.End();

            base.Draw(gameTime);
        }

        #endregion

    }
}

CharMovement.cs

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
namespace RPG
{
    public class CharMovement
    {
        //The asset name for the Sprite's Texture
        public string charSprite;
        //The Size of the Sprite (with scale applied)
        public Rectangle Size;
        //The amount to increase/decrease the size of the original sprite. 
        private float mScale = 1.0f;
        //The current position of the Sprite
        public Vector2 Position = new Vector2(0, 0);
        //The texture object used when drawing the sprite
        private Texture2D charTexture;


        //Load the texture for the sprite using the Content Pipeline
        public void LoadContent(ContentManager theContentManager, string theCharSprite)
        {
            //loads the image of the sprite
            charTexture = theContentManager.Load<Texture2D>(theCharSprite);

            theCharSprite = charSprite;

            //creates a new rectangle the size of the sprite
            Size = new Rectangle(0, 0, (int)(charTexture.Width * mScale), (int)(charTexture.Height * mScale));
        }

        //Update the Sprite and change it's position based on the set speed, direction and elapsed time.
        public void Update(GameTime theGameTime, Vector2 theSpeed, Vector2 theDirection)
        {
            //calculates the position of the sprite using the speed and direction from the MageChar class
            Position += theDirection * theSpeed * (float)theGameTime.ElapsedGameTime.TotalSeconds;
            if (Position.X < 0)
            {
                Position.X = 0;
            }
            if (Position.Y < 0)
            {
                Position.Y = 0;
            }
        }

        //Draw the sprite to the screen
        public void Draw(SpriteBatch theSpriteBatch)
        {
            //draw the sprite to the screen inside a rectangle     
            theSpriteBatch.Draw(charTexture, Position,
                new Rectangle(0, 0, charTexture.Width, charTexture.Height),
                Color.White, 0.0f, Vector2.Zero, mScale, SpriteEffects.None, 0);
        }
    }

}

MageChar.cs

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace RPG
{
    public class MageChar : CharMovement
    {
        //variable where sprite file name is stored
        string MageAssetName = "WalkingFront";
        //starting x position
        const int StartPositionX = 0;
        //starting y position
        const int StartPositionY = 0;
        //speed that the sprite will move on screen
        const int MageSpeed = 160;
        //move sprite 1 up/down when the arrow key is pressed
        const int MoveUp = 1;
        const int MoveDown = 1;
        const int MoveLeft = 1;
        const int MoveRight = 1;

        //used to store the current state of the sprite
        enum State
        {
            WalkingLeft,
            WalkingRight,
            WalkingFront,
            WalkingBack
        }
        //set to current state of the sprite. initally set to walking
        State CurrentState = State.WalkingFront;

        //stores direction of sprite
        Vector2 Direction = Vector2.Zero;

        //stores speed of sprite
        Vector2 Speed = Vector2.Zero;

        //stores previous state of keyboard
        KeyboardState PreviousKeyboardState;

        public void LoadContent(ContentManager theContentManager)
        {
            //sets position to the top left corner of the screen
            Position = new Vector2(StartPositionX, StartPositionY);

            //calls the load content method of the CharMovement class, passing in the content manager, and the name of the sprite image
            base.LoadContent(theContentManager, MageAssetName);
        }

        //checks state of the keyboard
        private void Update(KeyboardState aCurrentKeyboardState)
        {

            //run if the sprite is walking
            if (CurrentState == State.WalkingFront)
            {
                //sets direction and speed to zero
                Speed = Vector2.Zero;
                Direction = Vector2.Zero;

                //if left key is pressed, move left
                if (aCurrentKeyboardState.IsKeyDown(Keys.Left) == true)
                {
                    CurrentState = State.WalkingLeft;
                    MageAssetName = "WalkingLeft";
                    //speed of sprite movement
                    Speed.X -= MageSpeed;
                    //moves the sprite left
                    Direction.X -= MoveLeft;
                }
                //if right key is pressed, move right
                else if (aCurrentKeyboardState.IsKeyDown(Keys.Right) == true)
                {
                    CurrentState = State.WalkingRight;
                    MageAssetName = "WalkingRight";
                    //speed of sprite movement
                    Speed.X += MageSpeed;
                    //moves the sprite right
                    Direction.X += MoveRight;
                }
                //if up key is pressed, move up
                if (aCurrentKeyboardState.IsKeyDown(Keys.Up) == true)
                {
                    CurrentState = State.WalkingBack;
                    MageAssetName = "WalkingBack";
                    //speed of sprite movement
                    Speed.Y += MageSpeed;
                    //moves sprite up
                    Direction.Y += MoveUp;
                }
                //if down key is pressed, move down
                else if (aCurrentKeyboardState.IsKeyDown(Keys.Down) == true)
                {
                    CurrentState = State.WalkingFront;
                    MageAssetName = "WalkingFront";
                    //speed of sprite movement
                    Speed.Y -= MageSpeed;
                    //moves sprite down
                    Direction.Y -= MoveDown;
                }

            }
        }

        public void Update(GameTime theGameTime)
        {
            //obtains current state of the keyboard
            KeyboardState aCurrentKeyboardState = Keyboard.GetState();

            //calls in UpdateMovement and passes in current keyboard state
            Update(aCurrentKeyboardState);

            //set previous state to current state
            PreviousKeyboardState = aCurrentKeyboardState;

            //call update method of the charMovement class, passing in the gametime, speed and direction of the sprite
            base.Update(theGameTime, Speed, Direction);
        }


    }
}
1
I'm slightly horrified that MageChar IS a movement rather than having a movement variable... You must be learning though.Magus

1 Answers

1
votes

For the control of the Left and Down keys you decrement the Speed:

Speed.X -= MageSpeed; // Keys.Left
Speed.Y -= MageSpeed; // Keys.Down

Based on the conventional use of Speed this does not seem correct. If the player was moving right, for example and had a speed of 5. Then moving Left would cancel out this speed and set it to 0. Therefore the player would not be moving.

So it seems in all 4 cases, Speed should be incremented (+=).

Speed.X += MageSpeed; // Keys.Left
Speed.Y += MageSpeed; // Keys.Down

I would recommend have an else condition if the player is not pressing any of the direction keys. In this you can set the character MageChar to continue facing in the same direction that they were last moving in and set Speed to 0.

The code for your movement in MageChar has a potential issue:

You set CurrentState to State.WalkingLeft or State.WalkingRight depending on user input. Next there are checks for the up and down keys. If either of these are also pressed then CurrentState and MageAssetName are overwritten, if they had been assigned to in the left/right block of code.

This aligns with what you stated in the question:

When the down key is pressed, the sprite will move down, and stop when the key is released. However, when you press another arrow key once you have pressed the down or right key, the sprite image won't change direction. Also, the sprite images do not change when a new arrow key is pressed.

I would recommend first working more on movement. I recommend implementing independent checks for each movement direction/Key. Currently if the player presses the left key, the right key code is completely ignored, the same for the up and down keys respectively.

Your Direction values should be correct as you are incrementing and decrementing. However it makes intuitive sense that pressing both the left and right, or both the up and down, would cancel each others movements out. If you agree, four independent movement checks would resolve this.

There is something you may want to consider, when you have fixed this, which I hope you do. In the case that the player is moving diagonally, the will be moving both up or down and left or right. Here you may notice the player moving faster when moving diagonally. This can be explained by Pythagoras Theorem* a^2 + b^2 = c^2.

So, if the player is moving right by 4 and up by 4. The resultant speed would be 5.65. (4^2 + 4^2 = 32) -> sqrt(32) = 5.65. To resolve this, to 4 again, you will want to Normalize this variable. Which I can go into more detail on if requested.


*Pythagoras Theorem explained: Pythagaros Theorem Wikipedia Article