0
votes

I can easily rotate my sprite, but how could I rotate my rectangle for my collision (Thinking of using the Separating Axis Theorem, But I have no clue how to apply it) Help or Example would be appreciated :)

Game1 Class:

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;

namespace CombatTank
{

public class Game1 : Microsoft.Xna.Framework.Game
{
    //Declare Graphic Manager & Spritebatch
    GraphicsDeviceManager graphics;
    SpriteBatch spriteBatch;

    //Declare Player 1
    theBody player1TankBody;

    //Declare Player 2
    theBody player2TankBody;

    //Save Player 1 Position
    Vector2 savedPlayer1TankBodyPosition;

    //Save Player 2 Position
    Vector2 savedPlayer2TankBodyPosition;

    //Declare Keyboard States
    KeyboardState currentkeyboardState;
    KeyboardState previousKeyboardState;


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

    protected override void Initialize()
    {
        //TankBody Position Player 1
        Vector2 player1TankBodyPosition = new Vector2(200, 200);
        Vector2 player2TankBodyPosition = new Vector2(400, 200);

        //TankBody Scale 
        float player1TankBodyScale = 1.0F;
        float player2TankBodyScale = 1.0F;

        //TankBody Rotation
        float player1TankBodyRotation = 0.0F;
        float player2TankBodyRotation = 0.0F;

        //TankBody Color
        Color player1TankBodyColor = Color.Red;
        Color player2TankBodyColor = Color.Blue;

        //Create Tank
        player1TankBody = new theBody(player1TankBodyPosition,player1TankBodyScale, player1TankBodyRotation, player1TankBodyColor);
        player2TankBody = new theBody(player2TankBodyPosition, player2TankBodyScale, player2TankBodyRotation, player2TankBodyColor);

        base.Initialize();
    }

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

        //Load The Player 1 TankBody Texture
        Texture2D player1SpriteTankBody = Content.Load<Texture2D>("TankBody");
        player1TankBody.LoadContent(Content,"TankBody");

        //Extract Collision Data For Player 1
        player1TankBody.TankBodyTextureData = new Color[player1TankBody.Texture.Width * player1TankBody.Texture.Height];
        player1TankBody.Texture.GetData(player1TankBody.TankBodyTextureData);

        //Load The Player 2 TankBody Texture
        Texture2D player2SpriteTankBody = Content.Load<Texture2D>("TankBody");
        player2TankBody.LoadContent(Content, "TankBody");

        //Extract Collision Data For Player 2
        player2TankBody.TankBodyTextureData = new Color[player2TankBody.Texture.Width * player2TankBody.Texture.Height];
        player2TankBody.Texture.GetData(player2TankBody.TankBodyTextureData);

    }

    protected override void UnloadContent()
    {

    }


    protected override void Update(GameTime gameTime)
    {
        //Save Player 1 Postion
        savedPlayer1TankBodyPosition.X = player1TankBody.Position.X;
        savedPlayer1TankBodyPosition.Y = player1TankBody.Position.Y;

        //Save Player 2 Position
        savedPlayer2TankBodyPosition.X = player2TankBody.Position.X;
        savedPlayer2TankBodyPosition.Y = player2TankBody.Position.Y;

        //Updates Player 1
        UpdatePlayer1(gameTime);

        //Update Player 2
        UpdatePlayer2(gameTime);

        //Collision Player 1
        CollisionPlayer1(gameTime);

        //Collision Player 2
        CollisionPlayer2(gameTime);

        base.Update(gameTime);
    }

    private void UpdatePlayer1(GameTime gameTime)
    {
        //Save the previous state of the keyboard
        previousKeyboardState = currentkeyboardState;

        //Read the current state of the keyboard
        currentkeyboardState = Keyboard.GetState();

        //TankBody Movement
        if (currentkeyboardState.IsKeyDown(Keys.W))
        {
            //Move Tank Forward
            player1TankBody.Position.X -= 5 * (float)Math.Cos(player1TankBody.Rotation);
            player1TankBody.Position.Y -= 5 * (float)Math.Sin(player1TankBody.Rotation);
        }
        if (currentkeyboardState.IsKeyDown(Keys.S))
        {
            //Move Tank Backwards
            player1TankBody.Position.X += 5 * (float)Math.Cos(player1TankBody.Rotation);
            player1TankBody.Position.Y += 5 * (float)Math.Sin(player1TankBody.Rotation);

        }
        if (currentkeyboardState.IsKeyDown(Keys.A))
        {
            player1TankBody.Rotation -= 0.03f;
        }
        if (currentkeyboardState.IsKeyDown(Keys.D))
        {
            player1TankBody.Rotation += 0.03f;
        }

    }

    private void UpdatePlayer2(GameTime gameTime)
    {
        //Save the previous state of the keyboard
        previousKeyboardState = currentkeyboardState;

        //Read the current state of the keyboard
        currentkeyboardState = Keyboard.GetState();

        //TankBody Movement
        if (currentkeyboardState.IsKeyDown(Keys.Up))
        {
            //Move Tank Forward
            player2TankBody.Position.X -= 5 * (float)Math.Cos(player2TankBody.Rotation);
            player2TankBody.Position.Y -= 5 * (float)Math.Sin(player2TankBody.Rotation);
        }
        if (currentkeyboardState.IsKeyDown(Keys.Down))
        {
            //Move Tank Backward
            player2TankBody.Position.X += 5 * (float)Math.Cos(player2TankBody.Rotation);
            player2TankBody.Position.Y += 5 * (float)Math.Sin(player2TankBody.Rotation);
        }
        if (currentkeyboardState.IsKeyDown(Keys.Left))
        {
            player2TankBody.Rotation -= 0.03f;
        }
        if (currentkeyboardState.IsKeyDown(Keys.Right))
        {
            player2TankBody.Rotation += 0.03f;
        }

    }

    private void CollisionPlayer1(GameTime gameTime)
    {


        if (IntersectPixels(player1TankBody.BoundingBox, player1TankBody.TankBodyTextureData, player2TankBody.BoundingBox, player2TankBody.TankBodyTextureData))
        {
            player1TankBody.Position.X = savedPlayer1TankBodyPosition.X;
            player1TankBody.Position.Y = savedPlayer1TankBodyPosition.Y;

        }



    }

    private void CollisionPlayer2(GameTime gameTime)
    {
        if (IntersectPixels(player2TankBody.BoundingBox, player2TankBody.TankBodyTextureData, player1TankBody.BoundingBox, player1TankBody.TankBodyTextureData))
        {
            player2TankBody.Position.X = savedPlayer2TankBodyPosition.X;
            player2TankBody.Position.Y = savedPlayer2TankBodyPosition.Y;

        }
    }


    static bool IntersectPixels(Rectangle rectangleA, Color[] dataA, Rectangle rectangleB, Color[] dataB)
    {
        //Find top Bound of the Rectangle
        int top = Math.Max(rectangleA.Top, rectangleB.Top);
        int bottom = Math.Min(rectangleA.Bottom, rectangleB.Bottom);
        int left = Math.Max(rectangleA.Left, rectangleB.Left);
        int right = Math.Min(rectangleA.Right, rectangleB.Right);

        for (int y = top; y < bottom; y++)
        {
            for (int x = left; x < right; x++)
            {
                //Get Color of both Pixels
                Color colorA = dataA[(x - rectangleA.Left) + (y - rectangleA.Top) * rectangleA.Width];
                Color colorB = dataB[(x - rectangleB.Left) + (y - rectangleB.Top) * rectangleB.Width];

                //Both pixel are not completely Transparent
                if (colorA.A != 0 && colorB.B != 0)
                {
                    //Then an intersection is found 
                    return true;
                }

            }

        }


        //No Intersection
        return false;
    }





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

        spriteBatch.Begin();

        player1TankBody.Draw(spriteBatch);
        player2TankBody.Draw(spriteBatch);

        spriteBatch.End();

        base.Draw(gameTime);
    }
}
}

theBody Class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;

namespace CombatTank
{
class theBody
{
    //TankBody Texture
    private Texture2D texture;
    public Texture2D Texture
    {
        get
        { 
            return texture; 
        }
    }

    //TankBody Height
    private float height;
    public float Height
    {
        get
        {
            return height;
        }
    }

    //TankBody Width
    private float width;
    private float Width
    {
        get
        {
            return width;
        }

    }

    //TankBody Position
    public Vector2 Position;

    //TankBody Origin
    public Vector2 Origin;

    //TankBody Rotation
    public float Rotation = 0.0F;

    //TankBody Color
    public Color Color = Color.White;

    //TankBody Scale
    public float Scale = 1F;

    //TankBody BoundingBox
    public Rectangle BoundingBox
    {
        get
        {
            return new Rectangle((int)Position.X, (int)Position.Y, (int)texture.Width, (int)texture.Height);
        }
    }

    //TankBody color Data(Used For Pixel Collision)
    public Color[] TankBodyTextureData;

    //TankBody Constructor
    public theBody(Vector2 position,float scale,float rotation, Color color)
    {
        Position = position;
        Scale = scale;
        Rotation = rotation;
        Color = color;
    }

    //LoadContent
    public void LoadContent(ContentManager contentManager, string assetname)
    {
        texture = contentManager.Load<Texture2D>(assetname);
        Origin = new Vector2(Texture.Width / 2, Texture.Height / 2);
    }

    //Draw
    public virtual void Draw(SpriteBatch spriteBatch)
    {
        spriteBatch.Draw(texture, Position, null, Color, Rotation, Origin, Scale, SpriteEffects.None, 0);
    }

    //Update
    public void Update(GameTime gameTime)
    {

    }
}
}
3

3 Answers

1
votes

Unless this is practice for learning how to code physics engines I recommend using a free 2D collision/physics library rather than reinventing the wheel; Box2D comes to mind.

Just noticed that you are trying to do per-pixel collision between their textures based on transparency. Modern games (even really small ones) do collision and physics based on convexes, which allows you to have a more sophisticated impact result (if two pixels hit then what is the normal?).

0
votes

I understand in your question that you are using AABBs, and now you are trying to rotate the sprites, so now you need to rotate the AABB (That is, a OBB).

If Im not wrong and that is your case, one approach is what you suggested: SAT. But another approach is to use AABBs:

Note that an OBB its only an AABB defined in its own coordinate system (The optimal coordinate system that fits the AABB better to the object). You have two OOBBs (A and B), so you have two AABBs in two coordinate systems.

Get the AABB of B, and compute its AABB in the coordinate system of A (We can call this new AABB "C"). Check C and the AABB of A collision. Now do the same in reverse order (A's AABB in coordinate system of B (We call this new AABB "D"), and check collision with B's AABB).

If the two checks gets collision, the OBBs are in collision. See the picture:

enter image description here

0
votes

I haven't looked through your piece of code yet, but here is a good tutorial which explains the SAT with example Code. It's not C# but it's easy to convert ;)