2
votes

I've managed to get the values where I think they need to be getting so that the collision detection between snape and the platform bounding boxes takes place in the platform class. However it isn't working. There are no errors and I can't see where I'm going wrong. Your help would be much appreciated. My 3 classes are below.

Game1 Class

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

    Texture2D background;
    Movement character;
    Platform[] platforms;
    //private Vector2 SnapePosition = Vector2.Zero;

    public Game1()
    {
        graphics = new GraphicsDeviceManager(this);
        Content.RootDirectory = "Content";
        graphics.PreferredBackBufferHeight = 440;
        graphics.PreferredBackBufferWidth = 782;

    }


    protected override void Initialize()
    {
        // TODO: Add your initialization logic here
        platforms = new Platform[15];


        base.Initialize();
    }


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

        character = new Movement(Content.Load<Texture2D>("snape"), new Rectangle(0, 350, 50, 50));
        for (int i = 0; i < platforms.Length; i++)
        {
            platforms[i] = new Platform(
                Content.Load<Texture2D>("Platforms/lvl2_platform"), new Rectangle(i*100, 410, 100, 30), character.Snape, character.SnapePosition);
        }

        // TODO: use this.Content to load your game content here
        background = Content.Load<Texture2D>("Backgrounds/lvl2_background");
    }

    /// <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();

        //Allows the player to move
        character.Update();

        // 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);

        // TODO: Add your drawing code here

        spriteBatch.Begin();
        spriteBatch.Draw(background, Vector2.Zero, Color.White);
        character.Draw(spriteBatch);

        foreach (Platform platform in platforms)
        {
            platform.Draw(spriteBatch);
        }
        spriteBatch.End();

        base.Draw(gameTime);
    }
}
}

Player Class

class Player
{
    public Texture2D Snape;
    public Rectangle SnapePosition;


    public virtual void Update()
    {

    }

    public void Draw(SpriteBatch spriteBatch)
    {
        spriteBatch.Draw(Snape,SnapePosition,Color.White);
    }
}

class Movement : Player
{
        public Movement(Texture2D newSnape, Rectangle newSnapePosition)
    {
        Snape = newSnape;
        SnapePosition = newSnapePosition;
    }



        public override void Update()
        {
            KeyboardState keyBoard = Keyboard.GetState();

            if (keyBoard.IsKeyDown(Keys.A))
            {
                SnapePosition.X -= 5;
            }
            if (keyBoard.IsKeyDown(Keys.D))
            {
                SnapePosition.X += 5;
            }
            if (keyBoard.IsKeyDown(Keys.W))
            {
                SnapePosition.Y -= 5;
            }
            if (keyBoard.IsKeyDown(Keys.S))
            {
                SnapePosition.Y += 5;
            }

        }
}
}

Platform Class

class Platform
{
    Texture2D texture;
    Rectangle rectangle;
    Texture2D snape;
    Rectangle snapePosition;
    public Rectangle test;


    public enum CollisionPosition { None, Top, Bottom, Left, Right };
    public CollisionPosition collisionType;
    public bool inCollision;
    public int collisionDepth;

    public Platform(Texture2D newTexture, Rectangle newRectangle, Texture2D newSnape, Rectangle newSnapePos)
    { 
        texture = newTexture;
        rectangle = newRectangle;
        snapePosition = newSnapePos;
        snape = newSnape;
    }

    public void Draw(SpriteBatch spriteBatch)
    {
        spriteBatch.Draw(texture, rectangle, Color.White);
    }

    public void Collisions()
    {
        if (rectangle.Intersects(snapePosition))
        inCollision = true;
    }

    public void DetermineCollisionType()
    {
        if (inCollision == false)
        {
            collisionType = CollisionPosition.None;
            collisionDepth = 0;
        }
        else
        {
            // Determine the side of *least intersection* for snape
            int minOverlap = int.MaxValue;

            // Check the top side
            int tOverlap =
                (rectangle.Y + texture.Height / 2)
                - (snapePosition.Y - snape.Height / 2);
            if (tOverlap > 0 && tOverlap < minOverlap)
            {
                collisionType = CollisionPosition.Top;
                minOverlap = tOverlap;
            }

            // Check the bottom side
            int bOverlap =
                (snapePosition.Y + snape.Height / 2)
                - (rectangle.Y - texture.Height / 2);
            if (bOverlap > 0 && bOverlap < minOverlap)
            {
                collisionType = CollisionPosition.Bottom;
                minOverlap = bOverlap;
            }

            // Check the right overlap
            int rOverlap =
                (snapePosition.X + snape.Width / 2)
                - (rectangle.X - texture.Width / 2);
            if (rOverlap > 0 && rOverlap < minOverlap)
            {
                collisionType = CollisionPosition.Right;
                minOverlap = rOverlap;
            }

            // Check the left overlap
            int lOverlap =
                (rectangle.X + texture.Width / 2)
                - (snapePosition.X - snape.Width / 2);
            if (lOverlap > 0 && lOverlap < minOverlap)
            {
                collisionType = CollisionPosition.Left;
                minOverlap = lOverlap;
            }

            // Update the collision depth
            collisionDepth = minOverlap;
        }
    }

    public void SeparateSnape()
    {
        switch (collisionType)
        {
            case CollisionPosition.None:
                break;
            case CollisionPosition.Top:
                snapePosition.Y += collisionDepth;
                break;
            case CollisionPosition.Bottom:
                snapePosition.Y -= collisionDepth;
                break;
            case CollisionPosition.Right:
                snapePosition.X -= collisionDepth;
                break;
            case CollisionPosition.Left:
                snapePosition.X += collisionDepth;
                break;
        }
    }

    public void Update()
    {
        // Check for collision
        Collisions();

        // Determine collision type
        DetermineCollisionType();

        // Separate snape
        SeparateSnape();
    }

    public Rectangle getSnapePos
    {
        get { return snapePosition; }
    }
}

}
2

2 Answers

1
votes
protected override void Update(GameTime gameTime)
{
    // Allows the game to exit
    if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
        this.Exit();

    //Allows the player to move
    character.Update();

    // TODO: Add your update logic here


    base.Update(gameTime);
}

I do not see any updates regarding your platforms.

try adding

 foreach (Platform platform in platforms)
    {
        platform.Update();
    }
1
votes

Part of your problem appears to be that you don't know what the difference between a class and a struct is. A rectangle is a struct, meaning that when you pass it as an argument into any method it gets passed 'by value' meaning a copy of the value is made and it lives for as long as the method is running, after which it no longer exists. Classes on the other hand are reference types, so if you passed a class as an argument, you'd be passing it 'by reference' meaning the method would get a value telling it where to access the class. The difference here is that although the reference gets destroyed when the method ends, it is just a reference, not the value itself, unlike the struct.

On top of this, even if you were giving the platforms a reference to snape's position instead of just a value, when you move snape, you change the entire value of his position, not just parts of it, so it would be overwriting snape's reference, making the platform's references outdated anyway.

To be honest I think you would be better rethinking your design a bit. Firstly, try moving the keyboard management outside of the character, so the game essentially hooks onto one character and controls the movement of that one character. This is important because it means that the game itself knows who the main character is, what platforms the character can collide with and thus can perform collision checking with the platforms and the character. If the platforms can't change the character's position and the player doesn't know what all the platforms on the map are, then it seems more sensible to have the game itself monitoring that.

Here's an example:

protected override void Update()
        {
            KeyboardState keyboardState = Keyboard.GetState();
            Rectangle oldBounds = player.Bounds;
            if (keyboardState.IsKeyDown(Keys.W)) { player.Y--; }
            if (keyboardState.IsKeyDown(Keys.A)) { player.X--; }
            if (keyboardState.IsKeyDown(Keys.X)) { player.Y++; }
            if (keyboardState.IsKeyDown(Keys.D)) { player.X++; }
            foreach (Platform platform in platforms)
            {
                if (player.Bounds.Intersects(platform.Bounds))
                {
                    player.Bounds = oldBounds;
                    break;
                }
            }
        }

You'll want to make it a bit more sophisticated, but that's probably the most basic collision system available. Obviously if you're doing a platformer, you're probably going to want to be a bit more clever. I suggest you do as much research and experimentation as you can, also be sure to look up how to add breakpoints into your code and run your code line by line, as well as using the local and watch boxes to figure out exactly what's going on when your code runs. If you can identify what is happening and what should be happening, it will make things much easier to fix and understand.