I seem to be getting a null error and I can't for the life of me solve it. I'm basically creating a Tetris clone, but I can't seem to get this to work. I'm creating an array to hold the square shape, and then I am creating rectangles that are the same size, position etc as the square shape so I can use it for collision detection. I've also created rectangles for every tile space on the grid.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
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; 
using Microsoft.Xna.Framework.Net; 
using Microsoft.Xna.Framework.Storage; 
 
namespace tetris 
{ 
    class square : sprite 
    { 
        // declare random, to be used with the position of the square shape 
        Random Rand = new Random(); 
 
        public const int 
        START = 0, // newly created sprite 
        FALLING = 1, // square is falling 
        END = 2; // when sprite is finished 
 
 
        int _state;// state for the switch statement 
 
 
        //array for the square  
       public sprite[] squareArray = new sprite[4]; 
 
        //create a new rectangle array so each square tile has a rectangle around it 
       public Rectangle[] shapeTile = new Rectangle[4]; 
 
        const int startingWidth = 101; 
        const int startingHeight = 20; 
 
        const int height = 16; 
        const int width = 10; 
 
 
        // declare timer, to be used with the game clock for the square to be moving downward 
        int _timer; 
 
        // declare velocity for falling square 
        float _velocityX; 
        double _velocityY; 
 
        // declare rotation for square 
        double rotation; 
 
        // starting position for shape 
       public Vector2 startingPosition = new Vector2(205, 20); 
 
 
 
        // Constructor 
        public square(Texture2D tileImage, SpriteBatch sprite, Vector2 pos) 
            : base(tileImage, sprite, pos) 
        { 
            _velocityX = 1; 
 
            //starting position of the tile 
            pos = startingPosition; 
 
            //Initialises 2D array of tiles for square 
 
            //Array for the square! 
            for (int i = 0; i < 4; i++) 
            { 
                squareArray[i] = new sprite(tileImage, spriteBatch, startingPosition); 
            } 
 
 
            // put starting positions for each tile for the square 
            squareArray[0].position = new Vector2(startingPosition.X + 0, startingPosition.Y + 0); 
            squareArray[1].position = new Vector2(startingPosition.X + 35, startingPosition.Y + 0); 
            squareArray[2].position = new Vector2(startingPosition.X + 0, startingPosition.Y + 35); 
            squareArray[3].position = new Vector2(startingPosition.X + 35, startingPosition.Y + 35); 
 
            //shapeTile[0].X = squareArray[0].position.X; 
 
            //for (int q = 0; q < 4; q++) 
            //{ 
                //for (int w = 0; w < 4; w++) 
                //{ 
                    //shapeTile[0] = new Rectangle((startingWidth + (q * 35)), (startingHeight + (w * 35)), 35, 35); 
 
 
            ///// creating each rectangle so it has the same properties as the actual square (position, size etc) 
                    shapeTile[0] = new Rectangle((int)(squareArray[0].position.X), (int)(squareArray[0].position.Y), shapeTile[0].Width, shapeTile[0].Height); 
                    shapeTile[1] = new Rectangle((int)(squareArray[0].position.X), (int)(squareArray[1].position.Y), shapeTile[1].Width, shapeTile[1].Height); 
                    shapeTile[2] = new Rectangle((int)(squareArray[2].position.X), (int)(squareArray[2].position.Y), shapeTile[2].Width, shapeTile[2].Height); 
                    shapeTile[0] = new Rectangle((int)(squareArray[3].position.X), (int)(squareArray[3].position.Y), shapeTile[3].Width, shapeTile[3].Height); 
 
 
            { 
                _state = START; // start spawning square 
            } 
        } 
 
 
        public void Update(KeyboardState kbState) 
        { 
            switch (_state) 
            { 
                case START: // state 1, start 
                    // tiles for the square are visible 
                    squareArray[0].visible = true; 
                    squareArray[1].visible = true; 
                    squareArray[2].visible = true; 
                    squareArray[3].visible = true; 
                    _state = FALLING; 
                    break; 
 
 
                case FALLING: 
                    //movement of square using keyboard 
                    if (kbState.IsKeyDown(Keys.Right)) 
                    { 
                        squareArray[0].position.X += 35.0f * _velocityX; // X position moves 1 tile across 
                        squareArray[1].position.X += 35.0f * _velocityX; // X position moves 1 tile across 
                        squareArray[2].position.X += 35.0f * _velocityX; // X position moves 1 tile across 
                        squareArray[3].position.X += 35.0f * _velocityX; // X position moves 1 tile across 
                    } 
                    if (kbState.IsKeyDown(Keys.Left)) 
                    { 
                        squareArray[0].position.X -= 35.0f; // X position moves 1 tile across 
                        squareArray[1].position.X -= 35.0f; // X position moves 1 tile across 
                        squareArray[2].position.X -= 35.0f; // X position moves 1 tile across 
                        squareArray[3].position.X -= 35.0f; // X position moves 1 tile across                        
                    } 
                    if (kbState.IsKeyDown(Keys.Down)) 
                    { 
                        squareArray[0].position.Y += 35.0f; // X position moves 1 tile across 
                        squareArray[1].position.Y += 35.0f; // X position moves 1 tile across 
                        squareArray[2].position.Y += 35.0f; // X position moves 1 tile across 
                        squareArray[3].position.Y += 35.0f; // X position moves 1 tile across   
                    } 
 
                    //updating rectangles for square as it is moving 
                    shapeTile[0].X = (int)(squareArray[0].position.X); 
                    shapeTile[0].Y = (int)(squareArray[0].position.Y); 
                    shapeTile[1].X = (int)(squareArray[1].position.X); 
                    shapeTile[1].Y = (int)(squareArray[1].position.Y); 
                    shapeTile[2].X = (int)(squareArray[2].position.X); 
                    shapeTile[2].Y = (int)(squareArray[2].position.Y); 
                    shapeTile[3].X = (int)(squareArray[3].position.X); 
                    shapeTile[3].Y = (int)(squareArray[3].position.Y); 
 
                    //end square if bottom tile it goes below the grid 
                    if (squareArray[3].position.Y > 525) 
                    { 
                        _state = END; 
                    } 
 
                    //end square if bottom tile it goes below the grid 
                    if (squareArray[2].position.Y > 525) 
                    { 
                        _state = END; 
                    } 
                   break; 
 
                case END: 
                    // if square is finished, no longer see the tiles 
                    Finished = true; 
                    squareArray[0].visible = false; 
                    squareArray[1].visible = false; 
                    squareArray[2].visible = false; 
                    squareArray[3].visible = false; 
                    break; 
            } 
        } 
 
        //Draws tile using spritebatch 
        public void Draw(GameTime gameTime) 
        { 
            for (int i = 0; i < 4; i++) 
            { 
                squareArray[i].DrawTile(gameTime); 
            } 
        } 
    } 
}
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; 
using Microsoft.Xna.Framework.Net; 
using Microsoft.Xna.Framework.Storage; 
 
namespace tetris 
{ 
    class gameBoard 
    {  
        //create a new sprite array - tileArray 
       public sprite[,] tileArray; 
        const int height = 16; 
        const int width = 10; 
 
        // create a new rectangle array for the board 
       public Rectangle[,] recTile; 
 
 
        const int startingWidth = 101; 
        const int startingHeight = 20; 
 
        public gameBoard(SpriteBatch sprite, Texture2D tile) 
        { 
            //Initialises 2D array of tiles 
           tileArray = new sprite[width, height]; 
 
            //Create and position each tile 
            for (int x = 0; x < width; x++) 
            { 
                for (int y = 0; y < height; y++) 
                { 
                    tileArray[x, y] = new sprite(tile, sprite, new Vector2((startingWidth + (x * 35)), (startingHeight + (y * 35)))); 
                } 
            } 
 
 
            // create and position each rectangle at the exact coordinates as every tile on the grid 
            recTile = new Rectangle[width, height]; 
            for (int b = 0; b < width; b++) 
            { 
                for (int c = 0; c < height; c++) 
                { 
                    recTile[b, c] = new Rectangle((startingWidth + (b * 35)), (startingHeight + (c * 35)), 35, 35); 
 
                } 
            } 
 
        } 
 
        public void Update(GameTime gameTime) 
        { 
        } 
 
 
        //Draws each sprite in the grid, if it is visible 
        public void Draw(GameTime gameTime) 
        { 
            for (int x = 0; x < width; x++) 
            { 
                for (int y = 0; y < height; y++) 
                { 
 
                    if (tileArray[x, y].visible == true) 
                    { 
                        tileArray[x, y].DrawTile(gameTime); 
                    } 
                } 
            } 
        } 
    } 
}
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; 
using Microsoft.Xna.Framework.Net; 
using Microsoft.Xna.Framework.Storage; 
 
namespace tetris 
{ 
    /// <summary> 
    /// This is the main type for your game 
    /// </summary> 
    public class Game1 : Microsoft.Xna.Framework.Game 
    { 
        GraphicsDeviceManager graphics; 
        SpriteBatch spriteBatch; 
 
        Texture2D background; 
        Texture2D tile; 
 
        gameBoard board; 
 
        square shape; 
 
        int _state; 
        int score; 
 
        SpriteFont messageFont; 
 
        const int 
     INSERT_COIN = 0, 
     PLAYING = 1, 
     GAME_OVER = 2; 
 
        public Game1() 
        { 
            graphics = new GraphicsDeviceManager(this); 
 
            //Sets size of window 
            graphics.PreferredBackBufferHeight = 800; 
            graphics.PreferredBackBufferHeight = 600; 
            Content.RootDirectory = "Content"; 
        } 
 
        /// <summary> 
        /// Allows the game to perform any initialization it needs to before starting to run. 
        /// This is where it can query for any required services and load any non-graphic 
        /// related content.  Calling base.Initialize will enumerate through any components 
        /// and initialize them as well. 
        /// </summary> 
        protected override void Initialize() 
        { 
            // TODO: Add your initialization logic here 
 
           board = new gameBoard (spriteBatch, tile); 
 
           shape = new square(tile, spriteBatch, new Vector2(110, 10)); 
           //shape.shapeTile[0] = new Rectangle((int)(shape.squareArray[0].position.X), (int)(shape.squareArray[0].position.Y), shape.shapeTile[0].Width, shape.shapeTile[0].Height); 
           //shape.shapeTile[1] = new Rectangle((int)(shape.squareArray[0].position.X), (int)(shape.squareArray[1].position.Y), shape.shapeTile[1].Width, shape.shapeTile[1].Height); 
           //shape.shapeTile[2] = new Rectangle((int)(shape.squareArray[2].position.X), (int)(shape.squareArray[2].position.Y), shape.shapeTile[2].Width, shape.shapeTile[2].Height); 
           //shape.shapeTile[3] = new Rectangle((int)(shape.squareArray[3].position.X), (int)(shape.squareArray[3].position.Y), shape.shapeTile[3].Width, shape.shapeTile[3].Height); 
 
            base.Initialize(); 
        } 
 
        /// <summary> 
        /// LoadContent will be called once per game and is the place to load 
        /// all of your content. 
        /// </summary> 
        protected override void LoadContent() 
        { 
            // Create a new SpriteBatch, which can be used to draw textures. 
            spriteBatch = new SpriteBatch(GraphicsDevice); 
 
             
            //Load images 
            background = Content.Load<Texture2D>("Background"); 
            tile = Content.Load<Texture2D>("Tile"); 
            messageFont = this.Content.Load<SpriteFont>("font"); 
 
            // TODO: use this.Content to load your game content here 
        } 
 
        /// <summary> 
        /// UnloadContent will be called once per game and is the place to unload 
        /// all content. 
        /// </summary> 
        protected override void UnloadContent() 
        { 
            // TODO: Unload any non ContentManager content here 
        } 
 
        /// <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) 
        { 
            // Allows the game to exit 
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) 
                this.Exit(); 
 
            KeyboardState kbState = Keyboard.GetState(); 
 
            // switch statement for the gameplay 
 
            switch (_state) 
            { 
                case INSERT_COIN: 
 
                    shape = null; 
                    score = 0; 
                    if (kbState.IsKeyDown(Keys.Escape)) _state = PLAYING; 
                    break; 
 
                case PLAYING: 
                    if (shape == null) shape = new square(tile, spriteBatch, new Vector2(205, 20)); 
                    shape.Update(kbState); 
                    board.Update(gameTime); 
                    if (shape.Finished) score += 30; 
                    if (shape.Finished) shape = null; 
                    
                 
                // create and position each rectangle at the exact coordinates as every tile on the grid 
                    board.recTile = new Rectangle[10, 16]; 
                    for (int b = 0; b < 10; b++) 
                    { 
                        for (int c = 0; c < 16; c++) 
                        { 
                            board.recTile[b, c] = new Rectangle((101 + (b * 35)), (20 + (c * 35)), 35, 35); 
 
                        } 
                    } 
 
 
                    ////// TEST COLLISION ////// 
 
                    //for each tile in the square x 
                    for (int q = 0; q < 4; q++) 
                    { 
                        //for each tile in the square y 
                        for (int w = 0; w < 4; w++) 
                        { 
                            //for each tile on the board x axis 
                            for (int b = 0; b < 10; b++) 
                            { 
                                //for each tile on the board y axis 
                                for (int c = 0; c < 16; c++) 
                                { 
                                    if ((shape.shapeTile[0].Intersects(board.recTile[b, c]))) //) // ERROR IS HERE!
                                    { 
                                        board.tileArray[b, c].visible = true; 
                                        //shape.squareArray[1].visible = true; 
                                        //shape.squareArray[2].visible = true; 
                                        //shape.squareArray[3].visible = true; 
                                    } 
                                } 
                            } 
                        } 
                    } 
                    break; 
 
                case GAME_OVER: 
                    if (score == 40) _state = INSERT_COIN; 
                    break; 
 
                default: 
                    break; 
            } 
 
 
 
            // TODO: Add your update logic here 
 
            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) 
        { 
            GraphicsDevice.Clear(Color.CornflowerBlue); 
            spriteBatch.Begin(); 
 
            switch (_state) 
            { 
                case INSERT_COIN: 
                    spriteBatch.DrawString(messageFont, "Press ESC key to start game", new Vector2(220, 300), Color.White); 
                    break; 
 
                case PLAYING: 
                    spriteBatch.Draw(background, new Vector2(0, 0), Color.White); 
                    board.Draw(gameTime); 
                    if (shape != null) shape.Draw(gameTime); 
                    break; 
 
                case GAME_OVER: 
                    spriteBatch.Draw(background, new Vector2(0, 0), Color.White); 
                    break; 
 
                default: 
                    break; 
            } 
 
            spriteBatch.End(); 
            // TODO: Add your drawing code here 
 
            base.Draw(gameTime); 
        } 
    } 
 
}

Error at run-time is:
Object reference not set to an instance of an object.

Any help would be greatly appreciated.

DaveT

That's a lot of code!
Where does the error occur?

Line 157, Game1 Class.

I'll post my sprite class aswell.

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;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace tetris
{
    class sprite
    {
        Texture2D image;
        public Boolean visible = false;
        public SpriteBatch spriteBatch;
        public Vector2 position;
        public Boolean Finished = false;


        //Initailise tile using given image, spriteBatch and position
        public sprite(Texture2D tileImage, SpriteBatch sprite, Vector2 pos)
        {
            image = tileImage;
            spriteBatch = sprite;
            position = pos;
        }

        //Draws tile using spritebatch
        public void DrawTile(GameTime gameTime)
        {
            spriteBatch.Draw(image, position, Color.White); 
        }
    }
}

DaveT

You have commented out the lines that initialise the shape.shapeTile objects so shape.ShapeTile[0] == null.
Also, you are looping on q and w but dont seem to be using them..should they be used to determine which square you are checking against board.recTile[b, c] ?

I took away the comment lines for the shape.shapeTile objects in the initialize method (I also added that code to the loadContent just to be on the safe side), but it still doesn't work. [Still getting the same error - Object reference not set to an instance of an object.].

And it might sound confusing but this is how i've set it up and how I'm hoping to get it working:

The gameBoard has all the tiles on the grid already (called tileArray), but I have a boolean called "visible" set to false, so the tiles are invisible. What I am trying to do is create rectangles around every tile on the grid, and then create 4 rectangles around the square.Then i'll do the collision detection between the two rectangles, and if the square is at the bottom of the grid then i'll set the tiles in that position, "visible = true".

It might sound very long winded, but it's the way I understand it so i'm trying to get it to work.

DaveT

One error I think I have found is related to Game.LoadContent being called by Game.Initialise[/B].
I think this means that game1.spriteBatch is not set when Initialise executes.
Perhaps this is the reason you have the following in game1.Update.

case PLAYING:
    if (shape == null) shape = new square(tile, spriteBatch, new Vector2(205, 20));

Not sure if this is the cause of your current error though.

I'm not sure what you are saying above?? Do you mean I need to erase the above code from the update method?

DaveT

No, I am hinting at an error that I think you have not found yet and is not relevant to your current problem.
I think that spriteBatch in each of the sprites in board.tileArray is null because board is created before spriteBatch.

Re: original problem. What does inteli-sense show when you hover the mouse over each of the following when the error occurs: shape, shapeTile, board, recTile?

When I hover over where I get the error in debugging:

if ((shape.shapeTile[0].Intersects(board.recTile[b, c])))

shape.shapeTile[0], 'shape' has a value of null
board.recTile[b, c] has a value of b = 0, c = 0

DaveT

Clearly then, the error is due to shape being null.
What you need to do now is find out why shape is null.
Look to see if line 128 is executing.

Clearly then, the error is due to shape being null.
What you need to do now is find out why shape is null.
Look to see if line 128 is executing.

Ah thanks for that! I guess it's because shape was constantly being set to null.

I changed it to:

if (shape.Finished) shape = new square(tile, spriteBatch, new Vector2(205, 20));

And the run-time error has gone.

But now the collision detection between the rectangles refuses to work. Could it be because the values of "b" and "c" are 0 when in debugging mode/run-time?

I updated the code for the test collision to:

////// TEST COLLISION //////

                    //for each tile in the square x
                    for (int q = 0; q < 4; q++)
                    {
                        //for each tile in the square y
                        for (int w = 0; w < 4; w++)
                        {
                            //for each tile on the board x axis
                            for (int b = 0; b < 10; b++)
                            {
                                //for each tile on the board y axis
                                for (int c = 0; c < 16; c++)
                                {
                                    if ((shape.shapeTile[0].Intersects(board.recTile[b, c])) &&
                                        (shape.shapeTile[1].Intersects(board.recTile[b, c])) &&
                                        (shape.shapeTile[2].Intersects(board.recTile[b, c])) &&
                                        (shape.shapeTile[3].Intersects(board.recTile[b, c])) && (shape.Finished = true)) //)
                                    {
                                        board.tileArray[b, c].visible = true;
                                        board.tileArray[b, c].visible = true;
                                        board.tileArray[b, c].visible = true;
                                        board.tileArray[b, c].visible = true;
                                        
                                        shape.squareArray[0].visible = true;
                                        shape.squareArray[1].visible = true;
                                        shape.squareArray[2].visible = true;
                                        shape.squareArray[3].visible = true;
                                    }
                                }
                            }
                        }
                    }

But the tiles refuse to draw.

DaveT

Check the Height and Width properties of the shapeTile array elements.
I think the Square class constructor keeps these at the default value (which I think is 0).

[Edit] Not drawing could be to do with spriteBatch not being initialises as I said earlier.

Ok in the square constructor I changed it to:

///// creating each rectangle so it has the same properties as the actual square (position, size etc)
            shapeTile[0] = new Rectangle((int)(squareArray[0].position.X), (int)(squareArray[0].position.Y), 35, 35);
            shapeTile[1] = new Rectangle((int)(squareArray[0].position.X), (int)(squareArray[1].position.Y), 35, 35);
            shapeTile[2] = new Rectangle((int)(squareArray[2].position.X), (int)(squareArray[2].position.Y), 35, 35);
            shapeTile[0] = new Rectangle((int)(squareArray[3].position.X), (int)(squareArray[3].position.Y), 35, 35);

Where 35 is the dimensions of the tile. It still doesn't work so I believe that what you said about the spriteBatch being initialised before may be correct.

How would I go about fixing this?

My current initialize method looks like this:

protected override void Initialize()
        {
            // TODO: Add your initialization logic here

            shape = new square(tile, spriteBatch, new Vector2(110, 10));
            shape.shapeTile[0] = new Rectangle((int)(shape.squareArray[0].position.X), (int)(shape.squareArray[0].position.Y), 35, 35);
            shape.shapeTile[1] = new Rectangle((int)(shape.squareArray[1].position.X), (int)(shape.squareArray[1].position.Y), 35, 35);
            shape.shapeTile[2] = new Rectangle((int)(shape.squareArray[2].position.X), (int)(shape.squareArray[2].position.Y), 35, 35);
            shape.shapeTile[3] = new Rectangle((int)(shape.squareArray[3].position.X), (int)(shape.squareArray[3].position.Y), 35, 35);

            board = new gameBoard(spriteBatch, tile);
            
            base.Initialize();
        }

My load content:

protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            
            //Load images
            background = Content.Load<Texture2D>("Background");
            tile = Content.Load<Texture2D>("Tile");
            messageFont = this.Content.Load<SpriteFont>("font");


           // shape = new square(tile, spriteBatch, new Vector2(110, 10));

            shape.shapeTile[0] = new Rectangle((int)(shape.squareArray[0].position.X), (int)(shape.squareArray[0].position.Y), 35, 35);
            shape.shapeTile[1] = new Rectangle((int)(shape.squareArray[1].position.X), (int)(shape.squareArray[1].position.Y), 35, 35);
            shape.shapeTile[2] = new Rectangle((int)(shape.squareArray[2].position.X), (int)(shape.squareArray[2].position.Y), 35, 35);
            shape.shapeTile[3] = new Rectangle((int)(shape.squareArray[3].position.X), (int)(shape.squareArray[3].position.Y), 35, 35);


            board = new gameBoard(spriteBatch, tile);

            
            // TODO: use this.Content to load your game content here
        }

Would is simply be a case of moving a few lines around?

DaveT

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.