Josh-CO Dev

Solving the worlds problems one line of code at a time.

XNA – Let’s break down our simple overhead game and start making classes

Leave a comment


This is a continuation from https://joshcodev.wordpress.com/2012/10/25/xna-make-a-sprite-face-your-mouse/ . If you have not yet followed this tutorial, it might be a good time to check it out.

Our old code was simple and worked, so why split it out into classes? Well, mostly because it makes the code easier to work with as we add more code and expand the game. For example, what if we make the game two players? You’d have to create another texture, a second block of draw statements, a second block of update statements, etc. Or when we start to fire bullets. We don’t want to have to create X number of bullets in our constructor, show and hide them, track each one, etc. Classes allow us to easily move the code over to manageable classes that can be re-used.

Let’s start by creating a new class in our projet named sprite.cs. This class will hold our information that will be common across all sprites.
Let’s add our normal using statement.

using System;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;


Nothing too fancy here. Let’s add our local variables

       
 //private variables
        public Texture2D Texture;
        public ContentManager Content;
        public float Angle;
        public int spriteWidth;
        public int spriteHeight;
        public Vector2 Position;
        public Vector2 Origin;

        public Vector2 screenSize { get; set; }

I’ve tried to name these names that make sense. Texture is our sprite image, for now this is a static sprite, no sprite sheets. Content is our content manager so we can load our texture. Angle is the angle that our sprite is currently facing. Spritewidth and spriteheight are the dimensions of our sprite. Position is where our sprite is on the screen. Origin is a point in the image that the sprite will be rotated around. Screensize is the dimensions of our viewport.

Now let’s add our constructor.

        public Sprite(ContentManager content)
        {
            Content = content;
        }

Pretty easy. All we are doing is setting up our content manager. Now, let’s create our just as simple update method.

        public virtual void Update(GameTime gt)
        {

        }

The only thing worth pointing out here is that the method is declared as virtual. This means that we can override this method in any child classes and supply our own update logic. At this time, I do not have any update logic that is shared, so this is blank.
Finally, we have our draw statement, this should look familiar as it is the same as it was in our previous tutorial.

        public virtual void Draw(SpriteBatch sb)
        {
            sb.Draw(Texture, new Rectangle((int)Position.X, (int)Position.Y, spriteWidth, spriteHeight), null, Color.White, Angle, Origin, SpriteEffects.None, 0);
        }

That gives us everything that we need for our base class, but now we need to turn it into a player. So create a new class and name is player.cs and add the same using statements as above.

Be sure you inherit the class from Sprite.

    class Player : Sprite

In our global variables, let’s declare our keyboard and mouse states.

        KeyboardState keyState = new KeyboardState();
        KeyboardState prevKeyState = new KeyboardState();

        MouseState currMouse = new MouseState();
        MouseState prevMouseState = new MouseState();

The reason we declare them in our globals and not in the update method is so that we can hold the previous states of each device. This will allow us to prevent the player from doing something like holding the mouse button down and continually firing bullets. They will have to let go and re-click to fire a second bullet.
Now, let’s create our constructor.

        public Player(ContentManager content) : base(content)
        {
            //this.Content = content;
            Texture = Content.Load<Texture2D>("playerOver");

            spriteWidth = 32;
            spriteHeight = 32;

            Position = new Vector2(150, 150);
            Origin = new Vector2(spriteWidth / 2, spriteHeight / 2);
        }

First, notice that we call :base on this method. This means that on instantiation, it is going to go out and call the constructor of our sprite class. If you remember from above, all this does is load our content manager which is why the first line is commented out. Next, we load our texture which you can download from the previous project. We hardcode our dimensions to 32 x 32. I am defaulting my position to a set location and then setting the origin point to the center of the image.

Let’s move on to our update statement which is a bit more complicated.

        public override void Update(GameTime gt)
        {
            prevKeyState = keyState;
            keyState = Keyboard.GetState();

            prevMouseState = currMouse;
            currMouse = Mouse.GetState();

            Vector2 mouseLoc = new Vector2(currMouse.X, currMouse.Y);

            Vector2 direction = mouseLoc - Position;

            Angle = (float)(Math.Atan2(direction.Y, direction.X));

            if (keyState.IsKeyDown(Keys.A))
                Position.X -= 2;
            if (keyState.IsKeyDown(Keys.D))
                Position.X += 2;
            if (keyState.IsKeyDown(Keys.W))
                Position.Y -= 2;
            if (keyState.IsKeyDown(Keys.S))
                Position.Y += 2;

            //check our screen bounds
            //by default the origin point is the top left corner of the image. Since our origin point is in the center, we need to factor that here. 
            if (Position.X < 0 + Origin.X)
                Position.X = 0 + Origin.X;
            if (Position.X >= screenSize.X - Origin.X)
                Position.X = screenSize.X - Origin.X;
            if (Position.Y < 0 + Origin.Y)
                Position.Y = 0 + Origin.Y;
            if (Position.Y >= screenSize.Y - Origin.Y)
                Position.Y = screenSize.Y - Origin.Y;

            base.Update(gt);
        }

Ok, first we are setting our previousKeyboard state to the current state and then pulling the new keyboard state. Same thing for the mouse. Now, we do the same math that we did in the previous tutoral calculate our angle of rotation and to handle our sprite movement. At the bottom, we are adding some additional checks to make sure the sprite stays within the bounds of the screen. Since our origin point is in the middle of the sprite and not the top left, we have to add the origin values to our check so that the sprite will collide with the side of the screen properly. For example, our left check would allow half of your player sprite to leave the screen without this. Don’t forget to call your base.update though this does nothing currently.

On to our draw method…

        public override void Draw(SpriteBatch sb)
        {
            base.Draw(sb);
        }

Right now, we just have to call base.Draw and this will handle the drawing of our sprite!

Now, we need to clean up our original game.cs file. Get rid of all the code that we added in the last tutorial and start as a blank game.

In your global variables declare a new player object.

     Player player;

In your initialize code, instantiate your player object, pass in your content manager, and then set the screensize property.

            player = new Player(Content);
            player.screenSize = new Vector2(graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight);
            this.IsMouseVisible = true;

In the update section, call the player.Update method to handle our movement and rotations.

    player.Update(gameTime);

Finally, in our draw method, reference our player.draw.

    player.Draw(spriteBatch);

Now, this might seem like a lot of code, but it really makes life easier later on down the road. When we want to start creating enemies, we just inherit them from sprite and then handle the update logic. Everything else will just work. Next, we’ll add a bullet class and handle firing projectiles.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s