C# XNA 4.0 Rectangle Rotation Collision - c#

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)
{
}
}
}

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?).

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:

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

Related

Camera is stuttering after random amount of time

I have a problem that my camera starts to stutter when moving it after a random amount of time. This is not the case for the movement itself. That runs really smoothly. It is just hte camera that starts to stutter. Small notice I am working in monogame
Here is the code of the main
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace Cube_chaser
{
public class CubeChaserGame : Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Camera camera;
Map map;
BasicEffect effect;
private Vector2 mouseRotationBuffer;
private MouseState currentMouseState, previousMouseState;
public Vector3 moveVector = Vector3.Zero;
public CubeChaserGame()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
camera = new Camera(this, new Vector3(0.5f, 0.5f, 0.5f), Vector3.Zero, 5f);
Components.Add(camera);
effect = new BasicEffect(GraphicsDevice);
map = new Map(GraphicsDevice);
IsMouseVisible = false;
graphics.IsFullScreen = true;
previousMouseState = Mouse.GetState();
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
}
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 || Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
KeyboardState keyState = Keyboard.GetState();
currentMouseState = Mouse.GetState();
moveVector = Vector3.Zero;
//Handle the mouse movement for rotation
float deltaX, deltaY;
if (currentMouseState != previousMouseState)
{
//We need to save the mouse location for further use
deltaX = (currentMouseState.X - (GraphicsDevice.Viewport.Width / 2));
deltaY = (currentMouseState.Y - (GraphicsDevice.Viewport.Height / 2));
mouseRotationBuffer.X -= (0.09f * deltaX * dt);
mouseRotationBuffer.Y -= (0.09f * deltaY * dt);
if (mouseRotationBuffer.Y < MathHelper.ToRadians(-75.0f))
{
mouseRotationBuffer.Y = mouseRotationBuffer.Y - (mouseRotationBuffer.Y - MathHelper.ToRadians(-75.0f));
}
if (mouseRotationBuffer.Y > MathHelper.ToRadians(75.0f))
{
mouseRotationBuffer.Y = mouseRotationBuffer.Y - (mouseRotationBuffer.Y - MathHelper.ToRadians(75.0f));
}
camera.Rotation = new Vector3(-MathHelper.Clamp(mouseRotationBuffer.Y, MathHelper.ToRadians(-75.0f), MathHelper.ToRadians(75.0f)), MathHelper.WrapAngle(mouseRotationBuffer.X), 0);
deltaX = 0;
deltaY = 0;
}
try { Mouse.SetPosition(GraphicsDevice.Viewport.Width / 2, GraphicsDevice.Viewport.Height / 2); }
catch { }
if (keyState.IsKeyDown(Keys.W)) moveVector.Z = 1;
if (keyState.IsKeyDown(Keys.S)) moveVector.Z = -1;
if (keyState.IsKeyDown(Keys.A)) moveVector.X = 1;
if (keyState.IsKeyDown(Keys.D)) moveVector.X = -1;
if (keyState.IsKeyDown(Keys.Space) && camera.Position.Y >= 0.5f && camera.Position.Y <= 0.8f) moveVector.Y = 0.05f;
// if (camera.Position.Y == 0.8f&&camera.Position.Y>=0.5f)camera.moveVector.Y=-0.01f;
if (moveVector != Vector3.Zero)
{
//We don't want to make the player move faster when it is going diagonally.
moveVector.Normalize();
//Now we add the smoothing factor and speed factor
moveVector *= (dt * camera.cameraSpeed);
Vector3 newPosition = camera.PreviewMove(moveVector);
bool moveTrue = true;
if (newPosition.X < 0 || newPosition.X > Map.mazeWidth) moveTrue = false;
if (newPosition.Z < 0 || newPosition.Z > Map.mazeHeight) moveTrue = false;
foreach (BoundingBox boxes in map.GetBoundsForCell((int)newPosition.X, (int)newPosition.Z))
{
if (boxes.Contains(newPosition) == ContainmentType.Contains)
{
moveTrue = false;
}
}
if (moveTrue) camera.Move(moveVector);
previousMouseState = currentMouseState;
camera.Update(gameTime);
base.Update(gameTime);
}
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
map.Draw(camera, effect);
// TODO: Add your drawing code here
base.Draw(gameTime);
}
}
}
This is the code for the camera:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;
namespace Cube_chaser
{
public class Camera: GameComponent
{
#region Fields
public Vector3 cameraPosition;
public Vector3 cameraRotation;
public float cameraSpeed;
private Vector3 cameraLookAt;
#endregion
#region Properties
public Matrix Projection
{
get;
protected set;
}
public Matrix View
{
get
{
return Matrix.CreateLookAt(cameraPosition, cameraLookAt, Vector3.Up);
}
}
public Vector3 Position
{
get { return cameraPosition; }
set
{
cameraPosition = value;
UpdateLookAt();
}
}
public Vector3 Rotation
{
get { return cameraRotation; }
set
{
cameraRotation = value;
UpdateLookAt();
}
}
#endregion
#region Constructor
public Camera (Game game, Vector3 position, Vector3 rotation, float speed):base(game)
{
cameraSpeed = speed;
//Setup the projection Matrix
Projection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, game.GraphicsDevice.Viewport.AspectRatio, 0.05f, 100.0f);
MoveTo(position, rotation);
}
#endregion
#region Helper Methods
//Set the camera its position and rotation
public void MoveTo(Vector3 pos, Vector3 rot)
{
Position = pos;
Rotation = rot;
}
//Updating the look at vector
public void UpdateLookAt()
{
//Built a rotation matrix to rotate the direction we are looking
Matrix rotationMatrix = Matrix.CreateRotationX(cameraRotation.X) * Matrix.CreateRotationY(cameraRotation.Y);
// Build a look at offset vector
Vector3 lookAtOffset = Vector3.Transform(Vector3.UnitZ, rotationMatrix);
//Update our camera's look at the vector
cameraLookAt = (cameraPosition + lookAtOffset);
}
//Method to create movement and to check if it can move:)
public Vector3 PreviewMove(Vector3 amount)
{
//Create a rotation matrix to move the camera
Matrix rotate = Matrix.CreateRotationY(cameraRotation.Y);
//Create the vector for movement
Vector3 movement = new Vector3(amount.X, amount.Y, amount.Z);
movement = Vector3.Transform(movement, rotate);
// Give the value of the camera position +ze movement
return (cameraPosition+movement);
}
//Method that moves the camera when it hasnt'collided with anything
public void Move(Vector3 scale)
{
//Moveto the location
MoveTo(PreviewMove(scale), Rotation);
}
#endregion
/*public void Update(GameTime gameTime)
{
}*/
}
}
Any idea why the camera stutters after a while?
Thanks,
Jeromer
Forgot to post the answer to my problem for people having the same issue.
The following changes were made to make the problem go away:
in the constructor of the game1 class
IsFixedTimeStep = true;
TargetElapsedTime = TimeSpan.FromMilliseconds(16);
This results in fixed timesteps and thus a smooth calculation

C# XNA Collision detection with random falling sprite

i am new in XNA .i am trying to build a small 2D game where enemy(ball) are falling from the top of the screen randomly.the player can move inside the screen. what i want to do is when the player(spaceShip) collide with the ball the ball will remove from the screen.And i don't know how to do that. can anyone help me with this?
here's my code-
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 RandomSprite
{
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
TimeSpan timeSpan = TimeSpan.FromMilliseconds(2000);
Sprite spaceShip;
Texture2D ballTexture;
Texture2D backgroundtexture;
Vector2 ballPos = new Vector2(100f, 100f);
List<Sprite> ballList = new List<Sprite>();
float timer = 0f;
float dropInterval = .50f;
float speed = 4f;
Random random;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
protected override void Initialize()
{
// TODO: Add your initialization logic here
random = new Random();
base.Initialize();
}
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
backgroundtexture = Content.Load<Texture2D>("dungeon600x400");
spriteBatch = new SpriteBatch(GraphicsDevice);
ballTexture = Content.Load<Texture2D>("ball");
spaceShip = new Sprite();
spaceShip.Texture = Content.Load<Texture2D>("gugu");
// retrieve the height of the screen
int screenHeight = GraphicsDevice.Viewport.Height;
// find the center point of the screen along the x-axis
int screenCenterX = GraphicsDevice.Viewport.Width / 2;
spaceShip.Position = new Vector2(
screenCenterX - (spaceShip.Texture.Width / 2),
screenHeight - spaceShip.Texture.Height - 20);
}
private void HandleInput()
{
// Retrieve the current state of the keyboard
KeyboardState keyboardState = Keyboard.GetState();
Vector2 playerVelocity = Vector2.Zero;
// Check if the Left arrow key is pressed and change the velocity of the character accordingly
if (keyboardState.IsKeyDown(Keys.Left))
{
playerVelocity += new Vector2(-speed, 0);
}
// Check if the Right arrow key is pressed and change the velocity of the character accordingly
if (keyboardState.IsKeyDown(Keys.Right))
{
playerVelocity += new Vector2(speed, 0);
}
if (keyboardState.IsKeyDown(Keys.Up))
{
playerVelocity += new Vector2(0, -speed);
}
if (keyboardState.IsKeyDown(Keys.Down))
{
playerVelocity += new Vector2(0, speed);
}
// Apply the velocity to the character's position
spaceShip.Position += playerVelocity;
}
public void HandleFallingCake()
{
List<Sprite> toRemove = new List<Sprite>();
foreach (Sprite ball in ballList)
{
if (ball.Position.Y > (GraphicsDevice.Viewport.Height - 100))
toRemove.Add(ball);
else
ball.Position += new Vector2(0, speed);
}
if (toRemove.Count==1 )
{
foreach (Sprite cake in toRemove)
{
ballList.Remove(cake);
}
}
}
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
// TODO: Add your update logic here
timer += (float)gameTime.ElapsedGameTime.TotalSeconds;
if (timer >= dropInterval)
{
int xPos = random.Next(GraphicsDevice.Viewport.Width-50);
ballList.Add(new Sprite(ballTexture, new Vector2(xPos, -100)));
timer = 0f;
}
HandleFallingCake();
HandleInput();
timeSpan -= gameTime.ElapsedGameTime;
if (timeSpan <= TimeSpan.Zero)
{
timeSpan = TimeSpan.FromMilliseconds(2000);
}
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
// TODO: Add your drawing code here
spriteBatch.Begin();
//spriteBatch.Draw(backgroundtexture, new Rectangle(0,0,GraphicsDevice.Viewport.Width, GraphicsDevice.Viewport.Height), Color.White);
spaceShip.Draw(spriteBatch);
foreach (Sprite ball in ballList)
{
ball.Draw(spriteBatch);
}
spriteBatch.End();
base.Draw(gameTime);
}
}
}
and i have a subclass sprite.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace RandomSprite
{
class Sprite
{
public Texture2D Texture;
public Vector2 Position ;
public Sprite() { }
public Sprite(Texture2D texture, Vector2 position)
{
Texture = texture;
Position = position;
}
public void Draw(SpriteBatch spriteBatch)
{
if (Texture != null)
spriteBatch.Draw(Texture, Position, Color.White);
}
}
}
You describe the goal, but not ask questions.
If you want to know how to check the intersection of two objects, then you need to define for them bounds (class Rectangle) and check their intersection method Rectangle.Intersect. Anyway better adjust your question.

Increasing viewing distance?

What I'm trying to do is increase my viewing distance so that i can see objects beyond 10000f I've included a picture to help you understand what i mean, i want to set my viewing distance to, lets say 50000f so that i can see more objects which are farther away.
below is my camera code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Graphics;
namespace test1
{
public class ArcBallCamera
{
//rotation
public Matrix rotation = Matrix.Identity;
//camera pos.
public Vector3 position = Vector3.Zero;
// Simply feed this camera the position of whatever you want its target to be
public Vector3 targetPosition = Vector3.Zero;
public Matrix viewMatrix = Matrix.Identity;
public Matrix projectionMatrix = Matrix.Identity;
private float zoom = 8000.0f;
public float Zoom
{
get
{
return zoom;
}
set
{ // Keep zoom within range
zoom = MathHelper.Clamp(value, zoomMin, zoomMax);
}
}
private float horizontalAngle = MathHelper.PiOver2;
public float HorizontalAngle
{
get
{
return horizontalAngle;
}
set
{ // Keep horizontalAngle between -pi and pi.
horizontalAngle = value % MathHelper.Pi;
}
}
private float verticalAngle = MathHelper.PiOver2;
public float VerticalAngle
{
get
{
return verticalAngle;
}
set
{ // Keep vertical angle within tolerances
verticalAngle = MathHelper.Clamp(value, verticalAngleMin, verticalAngleMax);
}
}
private const float verticalAngleMin = 0.01f;
private const float verticalAngleMax = MathHelper.Pi - 0.01f;
private const float zoomMin = 0.1f;
private const float zoomMax = 8000.0f;
// FOV is in radians
// screenWidth and screenHeight are pixel values. They're floats because we need to divide them to get an aspect ratio.
public ArcBallCamera(float FOV, float screenWidth, float screenHeight, float nearPlane, float farPlane)
{
if (screenHeight < float.Epsilon)
throw new Exception("screenHeight cannot be zero or a negative value");
if (screenWidth < float.Epsilon)
throw new Exception("screenWidth cannot be zero or a negative value");
if (nearPlane < 0.1f)
throw new Exception("nearPlane must be greater than 0.1");
this.projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.ToRadians(FOV), screenWidth / screenHeight,
nearPlane, farPlane);
}
public void Update(GameTime gameTime)
{
// Start with an initial offset
Vector3 cameraPosition = new Vector3(0.0f, zoom, 0.0f);
// Rotate vertically
cameraPosition = Vector3.Transform(cameraPosition, Matrix.CreateRotationX(verticalAngle));
// Rotate horizontally
cameraPosition = Vector3.Transform(cameraPosition, Matrix.CreateRotationY(horizontalAngle));
position = cameraPosition + targetPosition;
this.LookAt(targetPosition);
// Compute view matrix
this.viewMatrix = Matrix.CreateLookAt(this.position,
this.position + this.rotation.Forward,
this.rotation.Up);
}
/// <summary>
/// Points camera in direction of any position.
/// </summary>
/// <param name="targetPos">Target position for camera to face.</param>
public void LookAt(Vector3 targetPos)
{
Vector3 newForward = targetPos - this.position;
newForward.Normalize();
this.rotation.Forward = newForward;
Vector3 referenceVector = Vector3.UnitY;
// On the slim chance that the camera is pointer perfectly parallel with the Y Axis, we cannot
// use cross product with a parallel axis, so we change the reference vector to the forward axis (Z).
if (this.rotation.Forward.Y == referenceVector.Y || this.rotation.Forward.Y == -referenceVector.Y)
{
referenceVector = Vector3.UnitZ;
}
this.rotation.Right = Vector3.Cross(this.rotation.Forward, referenceVector);
this.rotation.Up = Vector3.Cross(this.rotation.Right, this.rotation.Forward);
}
public Matrix[] SetupEffectDefaults(Model myModel)
{
Matrix[] absoluteTransforms = new Matrix[myModel.Bones.Count];
myModel.CopyAbsoluteBoneTransformsTo(absoluteTransforms);
foreach (ModelMesh mesh in myModel.Meshes)
{
foreach (BasicEffect effect in mesh.Effects)
{
effect.EnableDefaultLighting();
effect.Projection = projectionMatrix;
effect.View = viewMatrix;
}
}
return absoluteTransforms;
}
}
}
We don't see how you create your ArcBallCamera, but simply changing the last parameter of the constructor to 50000 should do the job, see CreatePerspectiveFieldOfView documentation here.

Having Difficulties Implementing Bullet/Enemy Collision Detection In A Simple Shooter (C# 2010/XNA 4.0)

Firstly, I would like to say sorry for making another thread asking the same question (more or less), but I am desperate for an answer and the other thread went dead.
I am currently working on a project for school in which I am attempting to make a simple 2d shooter. Problem is, I don't know how to implement collision detection between my two lists (being bullets and enemies as stated in title). I am very new to programming and have been using various tutorials as a guide, some tutorials say to use a nested for loop and if statement while I have seen others using an if statement, oh and I'm using the rectangle collision method not the pixel by pixel one I think it was called. Thanks in advance! :)
Here is my code:
Main:
namespace Software_Design_Major_Project
{
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Texture2D ship; // Declaring the sprite
Vector2 shipposition = Vector2.Zero; // Creating a vector for the sprite.
List<bullets> bullets = new List<bullets>(); // a list is created here so that there can be multiple copies of this item at once without writing the extra code.
Texture2D texture;
KeyboardState pastkey;
//bullet sound effect.
SoundEffect effect;
List<enemies> Enemies = new List<enemies>();
Random random2 = new Random();
float spawn = 0;
public enum GameState
{
MainMenu,
Playing,
}
GameState CurrentGameState = GameState.MainMenu;
int screenWidth = 600, screenHeight = 480;
cButton play;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
//changes width of screen to 600px.
graphics.PreferredBackBufferWidth = 600;
}
protected override void Initialize()
{
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
// This statement positions the ship.
ship = Content.Load<Texture2D>("ship"); // Loads the ship into the memory.
shipposition = new Vector2((graphics.GraphicsDevice.Viewport.Width / 2) - (ship.Width / 2), 420);
// loads bullet sprite
texture = Content.Load<Texture2D>("bullet");
graphics.PreferredBackBufferWidth = screenWidth;
graphics.PreferredBackBufferHeight = screenHeight;
graphics.ApplyChanges();
IsMouseVisible = true;
play = new cButton(Content.Load<Texture2D>("play"), graphics.GraphicsDevice);
play.setPosition(new Vector2(225, 220));
effect = Content.Load<SoundEffect>("laser");
}
protected override void UnloadContent()
{
}
protected override void Update(GameTime gameTime)
{
spawn += (float)gameTime.ElapsedGameTime.TotalSeconds;
// spawns enemy every second.
foreach (enemies enemy in Enemies)
enemy.update(graphics.GraphicsDevice);
MouseState mouse = Mouse.GetState();
switch (CurrentGameState)
{
case GameState.MainMenu:
if (play.isClicked == true)
CurrentGameState = GameState.Playing;
play.Update(mouse);
break;
case GameState.Playing:
if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.E))
{
Exit();
}
break;
}
// Allows the ship to move left and stops the ship going off the screen.
if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Left) && shipposition.X >= 0)
{
shipposition.X -= 7;
}// same as above except for the right direction.
if (Keyboard.GetState(PlayerIndex.One).IsKeyDown(Keys.Right) && shipposition.X < ((graphics.GraphicsDevice.Viewport.Width) - (ship.Width)))
{
shipposition.X += 7;
}
// this prevents the player from holding down space to spam bullets.
if (Keyboard.GetState().IsKeyDown(Keys.Space) && pastkey.IsKeyUp(Keys.Space))
{
bullets bullet = new bullets(texture);
// the ships coordinates are gathered from the top left hand corner of the sprites rectangle therefore 'new Vector2(shipposition.X + 32, shipposition.Y)' had to
// be added rather than just = 'shipposition' to avoid having the bullets shoot from the wing.
bullet.position = new Vector2(shipposition.X + 32, shipposition.Y);
bullets.Add(bullet);
effect.Play();
}
pastkey = Keyboard.GetState();
//calls upon the update method from the bullets class.
foreach (bullets bullet in bullets)
bullet.update();
LoadEnemies();
base.Update(gameTime);
}
public void LoadEnemies()
{
int randX = random2.Next(10, 540);
if (spawn <= 1)
{
spawn = 0;
//limits amount of enemies on screen to 5.
if (Enemies.Count() < 5)
Enemies.Add(new enemies(Content.Load<Texture2D>("enemy"), new Vector2(randX, -100)));
}
for (int i = 0; i < Enemies.Count; i++)
{
if (!Enemies[i].enemyVisble)
{
//removes the enemies when they go off screen.
Enemies.RemoveAt(i);
i--;
}
}
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
// calls draw method in bullets class
foreach (bullets bullet in bullets)
{
bullet.Draw(spriteBatch);
}
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend);
spriteBatch.Draw(ship, shipposition, Color.White); // draws ship sprite
switch (CurrentGameState)
{
case GameState.MainMenu:
play.draw(spriteBatch);
spriteBatch.Draw(Content.Load<Texture2D>("menu"), new Rectangle(0, 0, screenWidth, screenHeight), Color.White);
break;
case GameState.Playing:
break;
}
foreach (enemies enemy in Enemies)
{
enemy.draw(spriteBatch);
}
spriteBatch.End();
base.Draw(gameTime);
}
}
}
Bullets Class:
namespace Software_Design_Major_Project
{
class bullets // A new class needs to be created to allow for bullets.
{
public Texture2D texture;
public Vector2 position;
public bool isvisible;
public bullets(Texture2D newtexture)
{
texture = newtexture;
isvisible = false;
}
public void update()
{
position.Y -= 3; // velocity of the bullet
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Begin();
spriteBatch.Draw(texture, position, Color.White);
spriteBatch.End();
}
}
}
Enemies Class:
namespace Software_Design_Major_Project
{
public class enemies
{
public Texture2D enemyTexture;
public Vector2 enemyPosition;
public bool enemyVisble = true;
public float enemyMoveSpeed;
public int Value;
Random random = new Random();
int randY;
public enemies (Texture2D newEnemyTexture, Vector2 newEnemyPosition)
{
enemyTexture = newEnemyTexture;
enemyPosition = newEnemyPosition;
randY = random.Next(1, 4);
enemyMoveSpeed = randY;
enemyVisble = true;
Value = 100;
}
public void update(GraphicsDevice graphics)
{
enemyPosition.Y += enemyMoveSpeed;
if (enemyPosition.Y > 500)
enemyVisble = false;
}
public void draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(enemyTexture, enemyPosition, Color.White);
enemyVisble = true;
}
}
}
You have several ways to do it... one of them maybe add a radio property to enemies and bullets....
for (int bi=0; bi<bullets.count; )
{
bool should_destroy_bullet = false;
Bullet b = bullets[bi];
for (int ei=0; ei<enemies.count; )
{
Enemy e = ememies[ei];
if (b.radio + e.radio < Vector2.Distance(b.Pos, e.Pos)) // Check collision
{
e.Died();
enemies[ei] = enemies[enemies.Count-1];
enemies.RemoveAt(enemies.Count-1);
should_destroy_bullet = true; // This lets a bullet kill more than one enemy
} else ei++;
}
if (should_destroy_bullet) {
b.Died();
bullets[bi] = bullets[bullets.count-1];
bullets.RemoveAt(bullets.count-1);
} else bi++;
}
Or you can build a rectangle for each sprite and check if they intersects....
if (b.Bounds.Intersects(e.Bounds)) ....
The Pythagorean theorem could work for you.(A squared plus B squared = C squared)
Make your players and enemies have a radius.Put this in your constructor.
public float radius = 15;
Put the following in a update void/subclass so that it happens every second.
Make a float the X location of your player and subtract the X location of the bullet. Square it.
float XDist = Math.Pow(player1.location.X - bullet.location.X,2);
Make a float the Y location of your player and subtract the Y location of the bullet.Square it.
float YDist = Math.Pow(player1.location.X - bullet.location.X,2);
Make a float to figure out the square root of the sum of Xdist and Ydist.
float actualDist = Math.Sqrt(Xdist + Ydist);
Now if the radius of your player is less than the distance between the player and the bullet, the bullet has hit the player.
if(actualDist < radius)
bullethit == true;

XNA - Pong Clone - Reflecting ball when it hits a wall?

I'm trying to make the ball bounce off of the top and bottom 'Walls' of my UI when creating a 2D Pong Clone.
This is my Game.cs
public void CheckBallPosition()
{
if (ball.Position.Y == 0 || ball.Position.Y >= graphics.PreferredBackBufferHeight)
ball.Move(true);
else
ball.Move(false);
if (ball.Position.X < 0 || ball.Position.X >= graphics.PreferredBackBufferWidth)
ball.Reset();
}
At the moment I'm using this in my Ball.cs
public void Move(bool IsCollidingWithWall)
{
if (IsCollidingWithWall)
{
Vector2 normal = new Vector2(0, 1);
Direction = Vector2.Reflect(Direction,normal);
this.Position += Direction;
Console.WriteLine("WALL COLLISION");
}
else
this.Position += Direction;
}
It works, but I'm using a manually typed Normal and I want to know how to calculate the normal of the top and bottom parts of the screen?
Well, this is how I would handle it
public void CheckBallPositionAndMove()
{
if (ball.Position.Y <= 0 || ball.Position.Y >= graphics.PreferredBackBufferHeight)
ball.HandleWallCollision();
ball.Move();
if (ball.Position.X < 0 || ball.Position.X >= graphics.PreferredBackBufferWidth)
ball.Reset();
}
//In Ball.cs:
private void HandleWallCollision(Vector2 normal)
{
Direction.Y *= -1; //Reflection about either normal is the same as multiplying y-vector by -1
}
private void Move()
{
this.Position += Direction;
}
Note however that using this "discrete" collision detection, you wait until after the ball has moved past the top/bottom of the screen to detect a collision; collisions that occur "between" frames may be noticably off, especially if the ball is moving fast. This is especially a problem if you are using this collision-detection method to detect collision with a paddle, since, if the ball is moving fast enough, it is possible for the ball to move right through the paddle!
The solution to this problem is to use what is known as Continuous Collision Detection. CCD is usually significantly more complex than discrete collision detection; fortunately, pong is simple enough that doing CCD would only be slightly more complex. However, you'd still need a solid grasp of high-school algebra to solve the equations.
If you are still interested, there is a good explaination of CCD in this lecture, and this GameDev article goes a bit more in-depth. There are also many questions relating to it on SO.
You could change boolean IsCollidingWithWall with some enum like:
enum CollideType
{
None,
Vertical,
Horizontal
}
and check this type when creating normal.
Each of the boundaries in your world is a line. One side of the line is solid and the other is not. The normal you are trying to compute is one part of the equation for that line. It points toward the non-solid side of the line. The other part of the line equation is the distance from the line to the origin. The equation for the line can be found from two points on that line. You can define these two points based on the coordinates in your game space where you want a wall.
The normal is computed by rotating the line segment defined by the two points 90 degrees and then Normalizing.
public static Vector2 ComputeNormal(Vector2 point1, Vector2 point2)
{
Vector2 normal = new Vector2();
normal.X = point2.Y - point1.Y;
normal.Y = point1.X - point2.X;
normal.Normalize();
return normal;
}
You are using the preferred back buffer width and height to define your world space so your would use these to define the points used to compute the normals.
float left = 0.0f;
float right = graphics.PreferredBackBufferWidth;
float top = 0.0f;
float bottom = graphics.PreferredBackBufferHeight;
Vector2 topNormal = ComputeNormal(new Vector2(left, top), new Vector2(right, top));
Vector2 bottomNormal = ComputeNormal(new Vector2(right, bottom), new Vector2(left, bottom));
Note that the points must be given in clockwise order so that the normal points in the correct direction.
The following XNA 4.0 program demonstrates these concepts in use:
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
namespace WindowsGame
{
public class Ball
{
const int DIAMETER = 40;
const float RADIUS = DIAMETER * 0.5f;
const float MASS = 0.25f;
const int PIXELS = DIAMETER * DIAMETER;
static readonly uint WHITE = Color.White.PackedValue;
static readonly uint BLACK = new Color(0, 0, 0, 0).PackedValue;
Texture2D m_texture;
Vector2 m_position;
Vector2 m_velocity;
public Ball(GraphicsDevice graphicsDevice)
{
m_texture = new Texture2D(graphicsDevice, DIAMETER, DIAMETER);
uint[] data = new uint[PIXELS];
for (int i = 0; i < DIAMETER; i++)
{
float iPosition = i - RADIUS;
for (int j = 0; j < DIAMETER; j++)
{
data[i * DIAMETER + j] = new Vector2(iPosition, j - RADIUS).Length() <= RADIUS ? WHITE : BLACK;
}
}
m_texture.SetData<uint>(data);
}
public float Radius
{
get
{
return RADIUS;
}
}
public Vector2 Position
{
get
{
return m_position;
}
}
public Vector2 Velocity
{
get
{
return m_velocity;
}
set
{
m_velocity = value;
}
}
public void ApplyImpulse(Vector2 impulse)
{
Vector2 acceleration = impulse / MASS;
m_velocity += acceleration;
}
public void Update(float dt)
{
m_position += m_velocity; // Euler integration - innaccurate and unstable but it will do for this simulation
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(m_texture, DrawRectangle, Color.White);
}
private Rectangle DrawRectangle
{
get
{
int x = (int)Math.Round(m_position.X - RADIUS);
int y = (int)Math.Round(m_position.Y - RADIUS);
return new Rectangle(x, y, DIAMETER, DIAMETER);
}
}
}
public class Boundary
{
private Vector2 m_point1;
private Vector2 m_point2;
private Vector2 m_normal;
private float m_distance;
public Boundary(Vector2 point1, Vector2 point2)
{
m_point1 = point1;
m_point2 = point2;
m_normal = new Vector2();
m_normal.X = point2.Y - point1.Y;
m_normal.Y = point1.X - point2.X;
m_distance = point2.X * point1.Y - point1.X * point2.Y;
float invLength = 1.0f / m_normal.Length();
m_normal *= invLength;
m_distance *= invLength;
}
public Vector2 Normal
{
get
{
return m_normal;
}
}
public void PerformCollision(Ball ball)
{
float distanceToBallCenter = DistanceToPoint(ball.Position);
if (distanceToBallCenter <= ball.Radius)
{
ResolveCollision(ball);
}
}
public void ResolveCollision(Ball ball)
{
ball.Velocity = Vector2.Reflect(ball.Velocity, m_normal);
}
private float DistanceToPoint(Vector2 point)
{
return
m_normal.X * point.X +
m_normal.Y * point.Y +
m_distance;
}
}
public class World
{
Boundary m_left;
Boundary m_right;
Boundary m_top;
Boundary m_bottom;
public World(float left, float right, float top, float bottom)
{
m_top = new Boundary(new Vector2(right, top), new Vector2(left, top));
m_right = new Boundary(new Vector2(right, bottom), new Vector2(right, top));
m_bottom = new Boundary(new Vector2(left, bottom), new Vector2(right, bottom));
m_left = new Boundary(new Vector2(left, top), new Vector2(left, bottom));
}
public void PerformCollision(Ball ball)
{
m_top.PerformCollision(ball);
m_right.PerformCollision(ball);
m_bottom.PerformCollision(ball);
m_left.PerformCollision(ball);
}
}
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Matrix viewMatrix;
Matrix inverseViewMatrix;
Ball ball;
World world;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
IsMouseVisible = true;
}
protected override void Initialize()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
ball = new Ball(GraphicsDevice);
float right = Window.ClientBounds.Width * 0.5f;
float left = -right;
float bottom = Window.ClientBounds.Height * 0.5f;
float top = -bottom;
world = new World(left, right, top, bottom);
viewMatrix = Matrix.CreateTranslation(Window.ClientBounds.Width * 0.5f, Window.ClientBounds.Height * 0.5f, 0.0f);
inverseViewMatrix = Matrix.Invert(viewMatrix);
base.Initialize();
}
private void ProcessUserInput()
{
MouseState mouseState = Mouse.GetState();
Vector2 mousePositionClient = new Vector2((float)mouseState.X, (float)mouseState.Y);
Vector2 mousePositionWorld = Vector2.Transform(mousePositionClient, inverseViewMatrix);
if (mousePositionWorld != ball.Position)
{
Vector2 impulse = mousePositionWorld - ball.Position;
impulse *= 1.0f / impulse.LengthSquared();
ball.ApplyImpulse(-impulse);
}
}
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();
float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
ProcessUserInput();
ball.Update(dt);
world.PerformCollision(ball);
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, viewMatrix);
ball.Draw(spriteBatch);
spriteBatch.End();
base.Draw(gameTime);
}
}
}
Wouldn't you just take the position of the ball minus the position of the wall and then normalize that vector to get what you needed without hardcoding it?
Vector2 normal = Position - WallPosition;
normal.Normalize();
The rest of your code should just work the same.

Categories