Search code examples
classxnacollision-detection

XNA multi class collision


I am trying to make the enemy spawning / movement system for a side scrolling game.
What is supposed to happen is the enemies are to spawn and move right until they collide with a specific object then they will travel in the opposite direction.

I have done research and asked question to get me this far: I have made an enemy class and then made a list that can add more enemies and set their individual spawning positions. Then I have made an EnemyBlock class so I can set where the various collision objects will be (In this case they are just brick squares).

I am having some trouble
A. In making the enemies turn around when they leave the game screen.
And B. writing collision code for the enemies and enemyblocks as when I try and write collision code in the Game1.cs Update section under foreach (Enemy enemy in enemies) it doesn't pick the the enemyblock's rectangle. If that makes sense. Im sorry but I am pretty new to XNA code.

Game1.cs code

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

    public Rectangle bounds;

    List<Enemy> Enemies = new List<Enemy>();
    List<EnemyBlock> Blocks = new List<EnemyBlock>();

    public Game1()
    {
        graphics = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";
    }


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

        base.Initialize();
    }


    protected override void LoadContent()
    {
        spriteBatch = new SpriteBatch(GraphicsDevice);

        Blocks.Add(new EnemyBlock(Content.Load<Texture2D>("02Brick"), new Vector2(50, 100)));
        Enemies.Add(new Enemy(Content.Load<Texture2D>("Mario_Sprite"), new Vector2(100, 100), new Vector2(1, 0)));
        Blocks.Add(new EnemyBlock(Content.Load<Texture2D>("02Brick"), new Vector2(500, 100)));
        Blocks.Add(new EnemyBlock(Content.Load<Texture2D>("02Brick"), new Vector2(50, 200)));
        Enemies.Add(new Enemy(Content.Load<Texture2D>("Mario_Sprite"), new Vector2(100, 200), new Vector2(1, 0)));
        Blocks.Add(new EnemyBlock(Content.Load<Texture2D>("02Brick"), new Vector2(400, 200)));

        foreach (Enemy enemy in Enemies)
        {
           enemy.bounds = new Rectangle((int)(enemy.position.X - enemy.texture.Width), (int)(enemy.position.Y - enemy.texture.Height), enemy.texture.Width, enemy.texture.Height);
        }
        foreach (EnemyBlock block in Blocks)
        {
            block.bounds = new Rectangle((int)(block.position.X - block.texture.Width), (int)(block.position.Y - block.texture.Height), block.texture.Width, block.texture.Height);
        }

    }


    protected override void UnloadContent()
    {
        // TODO: Unload any non ContentManager content here
    }


    protected override void Update(GameTime gameTime)
    {
        if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
            this.Exit();

        foreach (Enemy enemy in Enemies)
        {
            if (!GraphicsDevice.Viewport.Bounds.Contains(enemy.bounds))
            {
                enemy.velocity = -enemy.velocity;
                enemy.position += enemy.velocity;

            }

            else
            {

                enemy.position += enemy.velocity;

            }
        }

        base.Update(gameTime);
    }


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

        spriteBatch.Begin();
        foreach (Enemy enemy in Enemies)
        {
            spriteBatch.Draw(enemy.texture, enemy.position, Color.White);
        }
        foreach (EnemyBlock block in Blocks)
        {
            spriteBatch.Draw(block.texture, block.position, Color.White);
        }
        spriteBatch.End();

        base.Draw(gameTime);
    }
}
}

Enemy class code

class Enemy
  {
      public Texture2D texture;
      public Rectangle bounds;
      public Vector2 position;
      public Vector2 velocity;

      public Enemy(Texture2D Texture, Vector2 Position, Vector2 Velocity)
      {
          texture = Texture;
          position = Position;
          velocity = Velocity;
      }
  }

EnemyBlock class code

class EnemyBlock
{
    public Texture2D texture;
    public Rectangle bounds;
    public Vector2 position;

    public EnemyBlock(Texture2D Texture, Vector2 Position)
    {
        texture = Texture;
        position = Position;
    }
}

Solution

  • Your code seems to be very well structured and organized. Makes it a lot easier to be able to find any of these problems your having so thanks and keep your code organized. It's a good habit and it makes larger projects much easier.

    Anyways I think I might have the answer to your problem.

    The reason your rectangles aren't being picked up is because you are initializing the rectangles for your enemies and blocks in your load content method.

    This is fine except that you don't appear to update the position of the rectangles. You update the velocity and position of the enemies and blocks though you check for collision with the bounding rectangles.

    This means that your enemies will be able to move off screen and through objects because your the draw position has moved but the rectangle hasn't meaning it won't detect the collision.

    I believe other problem is with specifying the rectangle position. I believe your code maybe a bit off but if it works fine then leave it. But if the collision detection is a little off then try the following. Your code

    enemy.bounds = new Rectangle((int)(enemy.position.X - enemy.texture.Width), (int)(enemy.position.Y - enemy.texture.Height), enemy.texture.Width, enemy.texture.Height);
    

    should look like

    enemy.bounds = new Rectangle(enemy.position.X, enemy.position.Y, enemy.texture.Width, enemy.texture.Height);
    

    based on how you are drawing your images.

    The constructor of the rectangle specifies the top left corner's x, y position and the width/height of the rectangle. Rectangle r = new Rectangle(100, 100, 200, 200) creates a rectangle at 100, 100 and 200 wide and 200 tall.

    This will match up with how you draw your enemies and blocks.

    And lastly the collision code is fairly simple:

    foreach(Enemy enemy in Enemies)
    {
      foreach(EnemyBlock block in Blocks)
      {
        if(enemy.bounds.Contains(block.bounds))
        {
          //what to do on collision
        }
      }
    }
    

    I hope this is all correct and helps.