0
\$\begingroup\$

I see to be running into a problem here. I know it's easier to show code samples so here you go.

This is my main game class

namespace WindowsGame4
{
    public enum MoveDirection { Left, Right, Up, Down, None}
    public enum GameState { MainMenu, LevelOne, PauseScreen }

    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;

        GameState gameState = GameState.LevelOne;

        LevelOne levelOne = new LevelOne();
        MainMenu mainMenu = new MainMenu();

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);

            Content.RootDirectory = "Content";
        }

        protected override void Initialize()
        {
            if(gameState == GameState.MainMenu)
            {
                this.IsMouseVisible = true;
                mainMenu.Initialize(gameState);
            }

            if(gameState == GameState.LevelOne)
            {
                levelOne.Initialize();
            }

            base.Initialize();
        }

        protected override void LoadContent()
        {
            float viewportWidth = GraphicsDevice.Viewport.TitleSafeArea.Width;
            float viewportHeight = GraphicsDevice.Viewport.TitleSafeArea.Height;

            //pass in content manager, so we can load our textures and content from the class instead of from the main game code
            if(gameState == GameState.MainMenu)
            {
                mainMenu.LoadContent(Content, viewportWidth, viewportHeight);
            }

            if(gameState == GameState.LevelOne)
            {
                levelOne.LoadContent(Content, viewportWidth, viewportHeight);
            }

            spriteBatch = new SpriteBatch(GraphicsDevice);
        }

        protected override void UnloadContent()
        {

        }

        protected override void Update(GameTime gameTime)
        {
            if (gameState == GameState.MainMenu)
            {
                mainMenu.Update(gameTime);
                gameState = mainMenu.ReturnGameState();
            }

            if (gameState == GameState.LevelOne)
            {
                levelOne.Update(gameTime);
            }

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            spriteBatch.Begin();

            if (gameState == GameState.MainMenu)
            {
                mainMenu.Draw(spriteBatch);
            }

            if (gameState == GameState.LevelOne)
            {
                levelOne.Draw(spriteBatch);
            }

            spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}

My idea is, I can use the gamestate enum to switch between what I load for the player, like a menu or the main gameplay part. However I'm not sure if that is the right way to go about building a game in XNA. I'm VERY new to this, both xna and programming and structuring a program.

The problem I'm running in to so far is when I try this, it sas "object reference not set to an instance of an object." I'm not sure what that mean, as it is referring to right before I attempt to draw the stuff happening in my levelone class.

EDIT for Shiro, Here is the level one class

namespace WindowsGame4 { class LevelOne { GameState gameState;

    Rizerd rizerd = new Rizerd();
    Player player = new Player();

    public void Initialize()
    {

    }

    public void LoadContent(ContentManager Content, float viewportWidth, float viewportHeight)
    {
        Vector2 position = new Vector2(viewportWidth / 2, viewportHeight / 2);
        Vector2 rizardPosition = new Vector2(viewportWidth / 3, viewportHeight / 3);
        player.Initialize(Content.Load<Texture2D>("warriorspritesheet"), Content.Load<Texture2D>("hitbox"), Content.Load<Texture2D>("horizontalSwordHitbox"), Content.Load<Texture2D>("VerticalSwordHitbox"), position, 4, 4);
        rizerd.Initialize(Content.Load<Texture2D>("rizerd"), Content.Load<Texture2D>("rizardBounding"), rizardPosition, 2, 2);
    }

    public void Update(GameTime gameTime)
    {
        rizerd.Update(gameTime);
        player.Update(gameTime);
    }

    public void Draw(SpriteBatch spriteBatch)
    {

        player.Draw(spriteBatch);
        rizerd.Draw(spriteBatch);

    }
}

}

As well as the player class and the sprite class that is the base class for player

PLAYER CLASS namespace WindowsGame4 { class Player : Sprites { string name; int health; int mana; int score; int damage = 34;

    MouseState mouseState;
    MouseState previousMouseState;

    bool IsAttacking = false;
    int attackFrameTime = 250;

    int mouseDelay = 350;
    public bool mouseUsed = false;
    int elapsedMouseTime;

    Texture2D horizontalSword;
    Texture2D verticalSword;

    Vector2 swordHitBoxLocationDown;
    Vector2 swordHitBoxLocationUp;
    Vector2 swordHitBoxLocationRight;
    Vector2 swordHitBoxLocationLeft;

    public Rectangle swordDown;
    public Rectangle swordUp;
    public Rectangle swordRight;
    public Rectangle swordLeft;

    KeyboardState debugPress; 
    bool debug;

    public bool playerCollide;

    //using initialize to pull the texture and frame count info into the GetFrames animation interfaces
    public void Initialize(Texture2D sprite, Texture2D boundingSprite, Texture2D horizontalSword, Texture2D verticalSword, Vector2 position, int frameRows, int frameColumns)
    {
        this.position = position;
        frameTime = 170;
        direction = MoveDirection.Down;
        speed = 3.0f;
        this.horizontalSword = horizontalSword;
        this.verticalSword = verticalSword;
        GetFrames(sprite, frameRows, frameColumns);
        SetBoundingBox(boundingSprite, position, sprite);
        SetSwordCollision();
    }
    public void Update(GameTime gameTime)
    {
        UpdateSwordCollision(gameTime);
        MovementAnimation(gameTime);

        //debug boolean switch
        debugPress = Keyboard.GetState();
        if(debugPress.IsKeyDown(Keys.F))
        {
            debug = true;
        }
        if(debugPress.IsKeyDown(Keys.G))
        {
            debug = false;
        }
    }

    public void Draw(SpriteBatch spriteBatch)
    {
        DrawFrame(spriteBatch, position);

        //debug, for checking hitbox location and activation
        if(playerCollide == true)
        {
            spriteBatch.Draw(playerBoundingSprite, playerBoundingPosition, Color.White);
        }
        else
        {

        }

    }

    //calculates the swords hitboxes based on the texture passed in for them, as well of the original sprites bounding position
    public void SetSwordCollision()
    {
        swordHitBoxLocationDown.Y = playerBoundingPosition.Y + playerBoundingSprite.Height - 2;
        swordHitBoxLocationDown.X = playerBoundingPosition.X + ((playerBoundingSprite.Width / 2 - verticalSword.Width / 2) + 4);
        swordDown = new Rectangle((int)swordHitBoxLocationDown.X, (int)swordHitBoxLocationDown.Y, verticalSword.Width, verticalSword.Height);

        swordHitBoxLocationLeft.Y = playerBoundingPosition.Y + ((playerBoundingSprite.Height / 2) - 3);
        swordHitBoxLocationLeft.X = playerBoundingPosition.X - horizontalSword.Width - 1;
        swordLeft = new Rectangle((int)swordHitBoxLocationLeft.X, (int)swordHitBoxLocationLeft.Y, horizontalSword.Width, horizontalSword.Height);

        swordHitBoxLocationUp.Y = playerBoundingPosition.Y - verticalSword.Height;
        swordHitBoxLocationUp.X = playerBoundingPosition.X + (playerBoundingSprite.Width / 2 - verticalSword.Width / 2);
        swordUp = new Rectangle((int)swordHitBoxLocationUp.X, (int)swordHitBoxLocationUp.Y, verticalSword.Width, verticalSword.Height);

        swordHitBoxLocationRight.Y = playerBoundingPosition.Y + (playerBoundingSprite.Height / 2) - 3;
        swordHitBoxLocationRight.X = playerBoundingPosition.X + playerBoundingSprite.Width;
        swordRight = new Rectangle((int)swordHitBoxLocationRight.X, (int)swordHitBoxLocationRight.Y, horizontalSword.Width, horizontalSword.Height);
    }

    //updates the sword hitbox's position, the textures position as well, when the sprite moves
    public void UpdateSwordCollision(GameTime gameTime)
    {
        if (direction == MoveDirection.Up && Idle == false)
        {
            swordHitBoxLocationDown.Y -= speed;
            swordDown.Y -= (int)speed;

            swordHitBoxLocationLeft.Y -= speed;
            swordLeft.Y -= (int)speed;

            swordHitBoxLocationRight.Y -= speed;
            swordRight.Y -= (int)speed;

            swordHitBoxLocationUp.Y -= speed;
            swordUp.Y -= (int)speed;
        }
        if (direction == MoveDirection.Left && Idle == false)
        {
            swordHitBoxLocationDown.X -= speed;
            swordDown.X -= (int)speed;

            swordHitBoxLocationLeft.X -= speed;
            swordLeft.X -= (int)speed;

            swordHitBoxLocationRight.X -= speed;
            swordRight.X -= (int)speed;

            swordHitBoxLocationUp.X -= speed;
            swordUp.X -= (int)speed;
        }
        if (direction == MoveDirection.Down && Idle == false)
        {
            swordHitBoxLocationDown.Y += speed;
            swordDown.Y += (int)speed;

            swordHitBoxLocationLeft.Y += speed;
            swordLeft.Y += (int)speed;

            swordHitBoxLocationRight.Y += speed;
            swordRight.Y += (int)speed;

            swordHitBoxLocationUp.Y += speed;
            swordUp.Y += (int)speed;
        }
        if (direction == MoveDirection.Right && Idle == false)
        {
            swordHitBoxLocationDown.X += speed;
            swordDown.X += (int)speed;

            swordHitBoxLocationLeft.X += speed;
            swordLeft.X += (int)speed;

            swordHitBoxLocationRight.X += speed;
            swordRight.X += (int)speed;

            swordHitBoxLocationUp.X += speed;
            swordUp.X += (int)speed;
        }
    }

    public void MouseInput(GameTime gameTime)
    {
        previousMouseState = mouseState;
        mouseState = Mouse.GetState();
        //elapsed mout time and mousedelay allow us to use gametime to limit button mashing of the attack button
        elapsedMouseTime += (int)gameTime.ElapsedGameTime.Milliseconds;
        //using a previous mouse state to register buttonstate release and current mousestate to register buttonstate pressed at the same time
        //prevents the system from registering the button being held down
        if (mouseUsed == false)
        {
            if (mouseState.LeftButton == ButtonState.Pressed && previousMouseState.LeftButton == ButtonState.Released)
            {
                //elapsed time here is used to time out the attack frame, when we press the mouse button, the frame is timed out, when it is timed out, our speed and frame goes back to normal--
                //--using previous frame.  also our isattacking goes to false at the end of the time.
                elapsedTime = 0;
                elapsedMouseTime = 0;
                IsAttacking = true;
                speed = 0;
                mouseUsed = true;
            }
        }

        if(mouseUsed == true)
        {
            Idle = true;
        }

        //only call moveobject if the mouse cooldown completes
        else if(mouseUsed == false)
        {
            MoveObject();
        }

        if(elapsedMouseTime > mouseDelay)
        {
            mouseUsed = false;
        }

        //if the attack frame is done, then set the speed to normal and go back to the frame you were at before the attack frame.
        //also set isattacking to false
        if (elapsedTime > attackFrameTime)
        {
            currentFrame = previousFrame;
            speed = 3.0f;
            IsAttacking = false;
        }
        //idle frames, these will be defaulted to based on MoveDirection enum and if the player is idle via no keyboard input
        if (direction == MoveDirection.Left && Idle == true)
        {
            if (IsAttacking == true)
            {
                currentFrame = 8;
            }
            else if (IsAttacking == false)
            {
                currentFrame = 0;
                previousFrame = currentFrame;
            }
        }
    }
    public void MovementAnimation(GameTime gameTime)
    {
        MouseInput(gameTime);
        //using elapsed time to measure how long a frame has been on screen
        elapsedTime += (int)gameTime.ElapsedGameTime.TotalMilliseconds;

        if (direction == MoveDirection.Right && Idle == true)
        {
            if(IsAttacking == true)
            {
                currentFrame = 9;
            }
            else if(IsAttacking == false)
            {
                currentFrame = 3;
                previousFrame = currentFrame;
            }
        }
        if (direction == MoveDirection.Up && Idle == true)
        {
            if(IsAttacking == true)
            {
                currentFrame = 11;
            }
            else if (IsAttacking == false)
            {
                currentFrame = 7;
                previousFrame = currentFrame;
            }
        }
        if (direction == MoveDirection.Down && Idle == true)
        {
            if(IsAttacking == true)
            {
                currentFrame = 10;
            }
            else if (IsAttacking == false)
            {
                currentFrame = 5;
                previousFrame = currentFrame;
            }
        }



        //movement frames paired with our MoveDirection enum
        //first we check if the player is not idle
        if (direction == MoveDirection.Down && Idle == false)
        {
            //then we set the frame based on what our direction is
            if (IsAttacking == true)
            {
                currentFrame = 10;
            }
            else if (IsAttacking == false)
            {
                currentFrame = 5;
                previousFrame = currentFrame;
            }

            //as long as the proper time has passed, we will go to the next frame
            if (elapsedTime > frameTime)
            {
                currentFrame = 4;
                previousFrame = currentFrame;
                //we wait for the proper amount of time to pass again, then we set the elapsed time to zero
                //then as long as we are receiving false for idle, the frame will go back to 5, and repeat
                if (elapsedTime > frameTime * 2)
                {
                    elapsedTime = 0;
                }
            }
        }
        if (direction == MoveDirection.Left && Idle == false)
        {
            if (IsAttacking == true)
            {
                currentFrame = 8;
            }
            else if (IsAttacking == false)
            {
                currentFrame = 0;
                previousFrame = currentFrame;
            }
            if (elapsedTime > frameTime)
            {
                currentFrame = 1;
                previousFrame = currentFrame;
                if (elapsedTime > frameTime * 2)
                {
                    elapsedTime = 0;
                }
            }
        }
        if (direction == MoveDirection.Right && Idle == false)
        {
            if (IsAttacking == true)
            {
                currentFrame = 9;
            }
            else if (IsAttacking == false)
            {
                currentFrame = 3;
                previousFrame = currentFrame;
            }
            if (elapsedTime > frameTime)
            {
                currentFrame = 2;
                previousFrame = currentFrame;
                if (elapsedTime > frameTime * 2)
                {
                    elapsedTime = 0;
                }
            }
        }
        if (direction == MoveDirection.Up && Idle == false)
        {
            if (IsAttacking == true)
            {
                currentFrame = 11;
            }
            else if (IsAttacking == false)
            {
                currentFrame = 7;
                previousFrame = currentFrame;
            }
            if (elapsedTime > frameTime)
            {
                currentFrame = 6;
                previousFrame = currentFrame;
                if (elapsedTime > frameTime * 2)
                {
                    elapsedTime = 0;
                }
            }
        }
    }
}

}

SPRITE CLASS

class Sprites : IMovement, IAnimation
{
    protected Texture2D sprite;
    protected bool Idle = true;
    protected Vector2 position;
    protected float speed;
    protected MoveDirection direction;

    protected KeyboardState currentButtonPress;
    protected KeyboardState previousButtonPress;

    protected int rows;
    protected int columns;
    protected int currentFrame;
    protected int previousFrame;
    protected int totalFrames;
    protected int elapsedTime;
    protected int frameTime;

    protected Texture2D playerBoundingSprite;
    public Rectangle playerBoundingRectangle;
    protected Vector2 playerBoundingPosition;

    //calculates sprites bounding position and size based on the texture atlas passed in and the position of the sprite itself
    public void SetBoundingBox(Texture2D boundingSprite, Vector2 position, Texture2D atlas)
    {
        playerBoundingSprite = boundingSprite;
        playerBoundingPosition.Y = position.Y + ((atlas.Height / (columns * 2)) - (boundingSprite.Height / 2));
        playerBoundingPosition.X = position.X + ((atlas.Width / (rows * 2)) - (boundingSprite.Width / 2));
        playerBoundingRectangle = new Rectangle((int)playerBoundingPosition.X, (int)playerBoundingPosition.Y, boundingSprite.Width, boundingSprite.Height);
    }

    //updates the sprites position and the direction of our movedirection enum
    public void MoveObject()
    {
        previousButtonPress = currentButtonPress;
        currentButtonPress = Keyboard.GetState();

        if(currentButtonPress.IsKeyDown(Keys.W))
        {
            Idle = false;
            direction = MoveDirection.Up;
            position.Y -= speed;
            playerBoundingPosition.Y -= speed;
            playerBoundingRectangle.Y -= (int)speed;
        }
        else if(currentButtonPress.IsKeyDown(Keys.A))
        {
            Idle = false;
            direction = MoveDirection.Left;
            position.X -= speed;
            playerBoundingPosition.X -= speed;
            playerBoundingRectangle.X -= (int)speed;
        }
        else if(currentButtonPress.IsKeyDown(Keys.S))
        {
            Idle = false;
            direction = MoveDirection.Down;
            position.Y += speed;
            playerBoundingPosition.Y += speed;
            playerBoundingRectangle.Y += (int)speed;
        }
        else if(currentButtonPress.IsKeyDown(Keys.D))
        {
            Idle = false;
            direction = MoveDirection.Right;
            position.X += speed;
            playerBoundingPosition.X += speed;
            playerBoundingRectangle.X += (int)speed;
        }
        else
        {
            Idle = true;
        }
    }

    public void GetFrames(Texture2D spriteSheet, int rows, int columns)
    {
        //when i load content for this player, Ill use the constructor parameters from the player class and pass them into the parameters for this method.
        sprite = spriteSheet;
        this.rows = rows;
        this.columns = columns;
        currentFrame = 0;
        totalFrames = this.rows * this.columns;
    }

    public void DrawFrame(SpriteBatch spriteBatch, Vector2 position)
    {


        //get the width and height of the current frame
        int width = sprite.Width / columns;
        int height = sprite.Height / rows;

        //calculates the current row we are on based on what frame we are on
        //calculates the current column we are on based on what frame we are on
        //for currentRow, the float it return will determine what row we are on
        //for current column, the remainder will determine which column we are on
        int currentRow = (int)((float)currentFrame / (float)columns);
        int currentColumn = currentFrame % columns;

        Rectangle sourceRectangle;
        Rectangle destinationRectangle;

        sourceRectangle = new Rectangle(width * currentColumn, height * currentRow, width, height);
        destinationRectangle = new Rectangle((int)position.X, (int)position.Y, width, height);

        spriteBatch.Draw(sprite, destinationRectangle, sourceRectangle, Color.White);
    }
}

}

\$\endgroup\$
  • \$\begingroup\$ @Shiro I've updated the information for you :) \$\endgroup\$ – Steven Apr 19 '15 at 16:14
  • \$\begingroup\$ Ah, Sorry about that. I'm trying to see what lines are giving me errors, but I'm still new. using VS2013, in debugging mode atm. all I seem to be getting are warnings. I see under the Locals section it shows the exception, but not sure how to show what its referring to \$\endgroup\$ – Steven Apr 19 '15 at 16:26
  • \$\begingroup\$ I think I see the problem, LoadContent() is being called once per game, so levelOne.LoadContent(...) is never being called, because the logical condition if(gameState == GameState.LevelOne) is not true at the start of the game \$\endgroup\$ – dimitris93 Apr 19 '15 at 16:29
  • \$\begingroup\$ Ah, then I may be confused on game structure with XNA... If loadcontent and initilize in the main game class can only be called once, then do I call my other classes loadcontent and initialize in the update method? \$\endgroup\$ – Steven Apr 19 '15 at 16:41
  • \$\begingroup\$ I know what you mean on the 2kb of ram, just trying to get into good habits when it comes to being efficient with loading and unloading content. So no matter how much data, it's all loaded at the very beginning? then unloaded as need? \$\endgroup\$ – Steven Apr 19 '15 at 16:52
1
\$\begingroup\$

Ah, then I may be confused on game structure with XNA... If loadcontent and initilize in the main game class can only be called once,

They are called once per device creation not per game start. If you re-size the underlying Windows window, the device is, and all content should be, explicitly destroyed.

then do I call my other classes loadcontent and initialize in the update method?

When the device is created, game.Initialize() is called. Anything already added to game.Components is Initialize()'d at this point. Objects that are created during game.Initialize() need to be explicitly Initialize()'d at the same time.

After game.Initialize() is finished, game.LoadContent() is called. Do not use the Device and/or Content before game.LoadContent() is called. Anything already added to game.Components is LoadContent()'d at this point. Objects that are created during game.LoadContent() need to be Initialize()'d and LoadContent()'d at the same time, since game.Initialize() will not be called again until the device is destroyed.

After game.LoadContent() is finished, any object you create needs to be Initialize()'d and LoadContent()'d at the same time. You can do this anywhere, at any time.

Static/singleton:
Don't get fooled into making everything a sub-sub-sub-sub-sub-class of GameObject that must get added to game.Components. Make a generic GameObjectContainer<> class and inherit from it when you need to make "buckets" of similar things. A GameObjectContainer will be nothing but a List and overrides of Initialize(), LoadContent(), Update() and Render() that all contain nearly-identical Foreach(...)'s.

The following basic shape applies to all kinds of content. An example Manager class for textures:

class TextureManager
{
   private static Dictionary<string, Texture2D> allTextures;
   public static Texture2D GetTexture(string name)
   {
      if (allTextures.containsKey(name))
      {
         return allTextures[name];
      }
      else
      {
         Texture2D newTexture;
         switch (name)
         {
            case "ShipHull":
              newTexture = new Texture2D(...);
              break;
            default:
              throw new Exception("Unknown texture name");
              break;
         }
         allTextures.add(name, newTexture);
      }
   }
   public static void StaticInit(Game game)
   {
       //Call whenever the backbuffer needs to be created
       //Load backbuffer and other essentials
   }
   public static void StaticDeinit()
   {
       //Call whenever the device is destroyed
       //Unload all children
   }
}

From anywhere:

draw(..., TextureManager.GetTexture("ShipHull")); //Created/loaded when first accessed
//You can access it early so it's ready before you actually need it,
//but you don't **have** to load everything at game start.

Edit: another handy singleton

public class Game1 : Microsoft.Xna.Framework.Game
{
   public static readonly Game TheGame;
   public Game1()
   {
      TheGame = this;
      ...
   }
   ...
}

From anywhere:

Game1.TheGame.GraphicsDevice.PresentationParameters.BackBufferHeight
Game1.TheGame.Content
//Instead of passing as arguments

It is entirely valid to have only two GameObjects added to game.Components. One for 2D/interface stuff and one for 3D/world stuff. If they are both GameObjectContainers and you have implemented content managers, there is no need to pass Game to every component constructor. This may also remove the need for the distinction between Initialize() and LoadContent(); if your objects don't inherit from GameObject and will be created after game.LoadContent() is called, you can just completely initialize them within the parameter-less constructor. All content is, or will be, available through the appropriate Manager. Just make sure to StaticInitialize() all of the Managers absolutely first.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.