My collision detection seems to be working, all be it some of the collisions it is detecting are odd as in it'll say that the collision was on the bottom of the platform when it isn't. However that isn't the main problem.
When the player collides with the platform, instead of moving to the surface of the platform he moves above it and with my gravity in place it looks like he is constantly bouncing. I can't figure out why.
If you guys could help it would be much appreciated. It could be something very simple I'm missing since I'm new to XNA and C#.
I will list my Game1, Player and Platoform classes below. (My collision detection is in the player class)
Thanks
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;
}
/// <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
platforms = new Platform[15];
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);
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 = new Movement(Content.Load<Texture2D>("snape"), new Rectangle(0, 360, 50, 50), platforms);
// 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 enum CollisionPosition { None, Top, Bottom, Left, Right };
public CollisionPosition collisionType;
public bool inCollision;
public int collisionDepth;
public Platform[] plat;
public int num;
public virtual void Update()
{
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(Snape, SnapePosition, Color.White);
}
}
class Movement : Player
{
public Movement(Texture2D newSnape, Rectangle newSnapePosition, Platform[] a)
{
Snape = newSnape;
SnapePosition = newSnapePosition;
plat = a;
}
public override void Update()
{
// Check for collision
Collisions();
// Determine collision type
//DetermineCollisionType();
// Separate snape
//SeparateSnape();
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;
// }
// if (SnapePosition.X < 0)
// SnapePosition.X = 0;
// if (SnapePosition.Y < 0)
// SnapePosition.Y = 0;
// if (SnapePosition.X > GraphicsDevice.Viewport.Width - Snape.Width)
// SnapePosition.X = GraphicsDevice.Viewport.Width - Snape.Width;
// if (SnapePosition.Y > GraphicsDevice.Viewport.Height - Snape.Height)
// SnapePosition.Y = GraphicsDevice.Viewport.Height - Snape.Height;
}
public void Collisions()
{
for (int i = 0; i < plat.Length; i++)
{
if (plat[i].rectangle.Intersects(SnapePosition))
{
inCollision = true;
num = i;
DetermineCollisionType();
}
}
}
public void DetermineCollisionType()
{
if (inCollision == false)
{
collisionType = CollisionPosition.None;
collisionDepth = 0;
}
else
{
// Determine the side of *least intersection* for snape
int minColDepth = int.MaxValue;
// Check the top side
int tColDepth = (plat[num].rectangle.Y + plat[num].texture.Height / 2) - (SnapePosition.Y - Snape.Height / 2);
if (tColDepth > 0 && tColDepth < minColDepth)
{
collisionType = CollisionPosition.Top;
minColDepth = tColDepth;
}
// Check the bottom side
int bColDepth = (SnapePosition.Y + Snape.Height / 2) - (plat[num].rectangle.Y - plat[num].texture.Height / 2);
if (bColDepth > 0 && bColDepth < minColDepth)
{
collisionType = CollisionPosition.Bottom;
minColDepth = bColDepth;
}
// Check the right overlap
int rColDepth = (SnapePosition.X + Snape.Width / 2) - (plat[num].rectangle.X - plat[num].texture.Width / 2);
if (rColDepth > 0 && rColDepth < minColDepth)
{
collisionType = CollisionPosition.Right;
minColDepth = rColDepth;
}
// Check the left overlap
int lColDepth = (plat[num].rectangle.X + plat[num].texture.Width / 2) - (SnapePosition.X - Snape.Width / 2);
if (lColDepth > 0 && lColDepth < minColDepth)
{
collisionType = CollisionPosition.Left;
minColDepth = lColDepth;
}
// Update the collision depth
collisionDepth = minColDepth;
SeparateSnape();
}
}
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;
}
}
}
Platform Class
class Platform
{
public Texture2D texture;
public Rectangle rectangle;
public Platform(Texture2D newTexture, Rectangle newRectangle)
{
texture = newTexture;
rectangle = newRectangle;
}
public void Update()
{
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(texture, rectangle, Color.White);
}
}
I fixed up your collision.
All of your depth calculations were incorrect. You were doing them based upon the center of the entity (player/platform) and using their texture sizes instead of their rectangle sizes.
You may have been thinking that everything draws from the middle, but by default it draws from the top left in XNA.
Here are the new depth calculations located in your "Movement" class under the "DetermineCollisionType()" function.
The top depth needs to work on the top of the platform (Y), and on the bottom of snape (Y + Height).
int tColDepth =
(SnapePosition.Y + SnapePosition.Height) - (plat[num].rectangle.Y);
The bottom depth needs to work on the bottom of the playform (Y + Height) and on the top of snape (Y).
int bColDepth =
(plat[num].rectangle.Y + plat[num].rectangle.Height) - (SnapePosition.Y);
The right depth needs to work on the left of the platform (X) and the right of snape (X + Width).
int rColDepth =
(SnapePosition.X + SnapePosition.Width) - (plat[num].rectangle.X);
The left depth needs to work on the right of the platform (X + Width) and on the left of Snape (X).
int lColDepth =
(plat[num].rectangle.X + plat[num].rectangle.Width) - (SnapePosition.X);
This will work from now on in. However you may want to use lists for your platform. Lists are easier for when/if you do a level editor as you don't have to say "I want 15 platforms".
Related
I'm writing a 2D puzzle game in MonoGame. I just added my first moving sprite to the program, and discovered that I'm getting about 10fps on it, and I haven't got the faintest idea why. I don't know if I can provide enough information to get help here, but I thought it was worth a shot. For reference, the moving object is a Ball.
Main update routine:
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
MouseState newState = Mouse.GetState();
if (newState.LeftButton == ButtonState.Pressed && oldState.LeftButton == ButtonState.Released)
{
MouseLeftClicked(newState.X, newState.Y);
}
oldState = newState;
if (gameState == GameState.Playing)
{
try
{
foreach (Ball ball in ballList)
{
ball.Update(gameTime);
}
}
catch(NullReferenceException)
{
}
}
// TODO: Add your update logic here
base.Update(gameTime);
}
Ball update routine:
public void Update(GameTime gameTime)
{
this.pos += this.motion * (float)gameTime.ElapsedGameTime.TotalSeconds;
}
Main Draw routine:
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
// TODO: Add your drawing code here
spriteBatch.Begin();
for (int x = 0; x < 8; x++)
{
for (int y = 0; y < 6; y++)
{
if (gameGrid[x, y] != null)
{
spriteBatch.Draw(backgroundTile, gameGrid[x, y].rect, Color.White);
}
}
}
//Draw menu
if (gameState == GameState.StartMenu)
{
spriteBatch.Draw(startButton, orbPosition, Color.White);
}
//Draw game while playing
if (gameState == GameState.Playing)
{
for (int x = 0; x < 8; x++)
{
for (int y = 0; y < 6; y++)
{
try
{
gameGrid[x, y].pipe.Draw(spriteBatch);
}
catch (NullReferenceException)
{
continue;
}
}
}
foreach (Wheel wheel in wheelList)
{
wheel.Draw(spriteBatch);
}
foreach (Ball ball in ballList)
{
ball.Draw(spriteBatch);
}
}
spriteBatch.End();
base.Draw(gameTime);
}
Ball Draw routine:
public void Draw(SpriteBatch spriteBatch)
{
int sprite = (int)color;
Rectangle sourceRect = new Rectangle(sprite * spriteSize, 0, spriteSize, spriteSize);
Rectangle ballRect = new Rectangle((int)this.pos.X, (int)this.pos.Y, spriteSize * scale, spriteSize * scale);
//spriteBatch.Begin();
spriteBatch.Draw(this.texture, ballRect, sourceRect, Color.White);
//spriteBatch.End();
}
I tested your code and it does not lag when providing a texture. I had to make slight modification to your code in order to make it work since you omitted parts from it. I suppose the omitted parts may be responsible but it is unlikely. I don't know on what kind of machine you are testing this but, I will provide some recommendations in order to resolve the problem you have locally.
When writing performant code, forget everything you know about 'object oriented behaviour' and think data. Data oriented design is all about sticking data that belongs together into big chunks and processing it all at once. This is much faster. In many cases as for game design, when there is one, there are many. Use that to your advantage and pass the whole array and act upon it directly on the spot.
Avoid nesting exceptions and iterative loops if possible. Exceptions are expensive when they occur, they should only be used when it is very exceptional, or if it is extremely unusual that the case actually does occur, and you wish to handle this 'edge' case by throwing an exception to force consumers of the code to handle it.
for (int x = 0; x < 8; x++)
{
for (int y = 0; y < 6; y++)
{
try
{
gameGrid[x, y].pipe.Draw(spriteBatch);
}
catch (NullReferenceException)
{
continue;
}
}
}
Catching the Null Reference Exception that you nested in the two for loops might be a bad idea. If you need to throw from one of these, and that allows you to draw the next consider why the throw is necessary if you wish to keep the code as it is. If it's there to catch empties in gameGrid or pipe, consider that a constructor should always put the item in a valid state and that the list should always be 'complete'. If an element stops existing it should not be in the list anymore. Otherwise, if one failure means all fail move the try block outside. This is more common.
Profiling your application is a mechanism that helps you find where things are slower than you expected and sometimes even why. Here's a reference on how to do this in visual studio. Beginners Guide to Performance Profiling as well as Walkthrough: Profiling Applications.
That said, none of these would slow your application down to the sort of degree you are describing. Therefore I advice you to edit your question and include other relevant sections of the code that may be the cause. I have attached below a sample constructed from your small example with some small modifications in order to simulate your environment in a much more extreme manner. This example draws 100 rectangles of which each one is 50x50 and they all rescale and move like your application does as well. These are 100 tiles, if this is slow for you on its own you should definitely look into the profiler topic above if you are using visual studio or try getting the latest binaries from Mono Game's official website or your latest graphics card drivers.
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
public IList<Ball> Balls { get; private set; }
/// <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);
var rand = new Random();
Balls = new List<Ball>(5);
for(var iii = 0; iii < 100; ++iii)
Balls.Add(new Ball(GraphicsDevice, new Vector2(rand.Next(50, 500), rand.Next(50, 500))));
}
/// <summary>
/// UnloadContent will be called once per game and is the place to unload
/// all content.
/// </summary>
protected override void UnloadContent()
{
}
/// <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)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
foreach (var ball in Balls)
ball.Update(gameTime);
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();
foreach (var ball in Balls)
ball.Draw(spriteBatch);
spriteBatch.End();
base.Draw(gameTime);
}
}
public class Ball
{
public Texture2D Texture { get; }
public Vector2 Position { get; private set; }
public double Scale { get; private set; }
public Ball(GraphicsDevice gd, Vector2 initialPosition)
{
Texture = new Texture2D(gd, 50, 50);
Position = initialPosition;
var data = new Color[100*100];
for (var iii = 0; iii < data.Length; ++iii) data[iii] = Color.YellowGreen;
Texture.SetData(data);
}
private bool _increaseScale, _increaseX, _increaseY;
public void Update(GameTime gameTime)
{
if (Scale < 1)
_increaseScale = true;
else if (Scale > 4)
_increaseScale = false;
if (Position.X < 50)
_increaseX = true;
else if (Position.X > 500)
_increaseX = false;
if (Position.Y < 50)
_increaseY = true;
else if (Position.Y > 500)
_increaseY = false;
Scale += (_increaseScale ? 1.5 : -1.5) * gameTime.ElapsedGameTime.TotalSeconds;
Position += new Vector2((float)((_increaseX ? 100 : -100)*gameTime.ElapsedGameTime.TotalSeconds), (float)((_increaseY ? 100 : -100)*gameTime.ElapsedGameTime.TotalSeconds));
}
public void Draw(SpriteBatch spriteBatch)
{
var source = new Rectangle(0, 0, Texture.Height, Texture.Width);
var dest = new Rectangle((int)Position.X, (int)Position.Y, Texture.Width * (int)Scale, Texture.Height* (int)Scale);
spriteBatch.Draw(Texture, dest, source, Color.White);
}
}
I'm learning to program in C# using monodevelop and monogame. I'm fairly familiar with C and Java but in honesty, I'm still learning to think like an Object Oriented programmer. I wanted to use the following class to gather together relevant attributes of a sprite, together with methods to move, draw, accelerate, detect collisions etc. to tidy up the game code. This class appears in a file SpriteObject.cs and compiles without errors on build.
namespace MyFirstGame
{
class SpriteObject
{
private Texture2D Texture;
private Vector2 Position;
private Vector2 Velocity;
public SpriteObject(Texture2D texture, Vector2 position)
{
this.Texture = texture;
this.Position = position;
this.Velocity = Vector2.Zero;
}
public SpriteObject(Texture2D texture, Vector2 position, Vector2 velocity)
{
this.Texture = texture;
this.Position = position;
this.Velocity = velocity;
}
public Rectangle BoundingBox
{
get
{
return new Rectangle(
(int)Position.X,
(int)Position.Y,
Texture.Width,
Texture.Height);
}
}
public void Move(SpriteObject sprite)
{
this.Position += this.Velocity;
}
public void BounceX(SpriteObject sprite)
{
this.Velocity.X *= -1;
}
public void BounceY(SpriteObject sprite)
{
this.Velocity.Y *= -1;
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(Texture, Position, Color.White);
}
}
}
I'm am trying to use this class in the following code (which is a bastard mix of the untidy, but running, code I'm trying to clean up and the new class I'm trying to do it with). Note particularly the lines marked with A> and B>.
namespace MyFirstGame
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Game
{
GraphicsDeviceManager graphics; // All default to private
KeyboardState oldKeyState;
SpriteBatch spriteBatch;
Boolean gameOn=true;
A> SpriteObject ballSprite, starSprite; // A couple of sprites...
Texture2D texStar, texBall; // This is a texture we can render.
public Game1()
{
graphics = new GraphicsDeviceManager(this);
graphics.PreferredBackBufferHeight = 900-32; // Force window size or it won't work...
graphics.PreferredBackBufferWidth = 1600; // Force window size
Content.RootDirectory = "Content";
graphics.IsFullScreen = true;
}
/// <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
oldKeyState = Keyboard.GetState();
base.Initialize();
}
/// <summary>
/// LoadContent will be called once per game and is the place to load
/// all of your content.
/// </summary>
// Set the coordinates to draw the sprite at.
Vector2 starPosition = Vector2.Zero;
Vector2 ballPosition = new Vector2(200, 0);
// Store some information about the sprite's motion.
Vector2 starSpeed = new Vector2(10.0f, 10.0f);
Vector2 ballSpeed = new Vector2(10.0f, 0.0f);
protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);
texStar = Content.Load<Texture2D>("star");
texBall = Content.Load<Texture2D>("ball");
B> ballSprite = new SpriteObject(texBall, Vector2(200, 0), Vector2(10.0f, 0.0f)); // create two sprites
B> starSprite = new SpriteObject(texStar, Vector2.Zero, Vector2(10.0f, 10.0f));
}
/// <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)
{
// Move the sprite by speed.
KeyboardState newKeyState = Keyboard.GetState();
if (newKeyState.IsKeyDown(Keys.Escape))
Exit();
if (collisionDetected()) gameOn = false; // If there's a collision then no movement.
//Could do with putting this in a seperate method or class, if there isn't one already...
if (gameOn)
{
if (newKeyState.IsKeyDown(Keys.PageUp) && oldKeyState.IsKeyUp(Keys.PageUp))
{
starSpeed.X *= 1.1f;
starSpeed.Y *= 1.1f;
}
else if (newKeyState.IsKeyDown(Keys.PageDown) && oldKeyState.IsKeyUp(Keys.PageDown))
{
starSpeed.X *= 0.9f;
starSpeed.Y *= 0.9f;
}
else if (newKeyState.IsKeyDown(Keys.Up) && oldKeyState.IsKeyUp(Keys.Up))
{
starSpeed.Y += 1.0f;
}
else if (newKeyState.IsKeyDown(Keys.Down) && oldKeyState.IsKeyUp(Keys.Down))
{
starSpeed.Y -= 1.0f;
}
else if (newKeyState.IsKeyDown(Keys.Left) && oldKeyState.IsKeyUp(Keys.Left))
{
starSpeed.X -= 1.0f;
}
else if (newKeyState.IsKeyDown(Keys.Right) && oldKeyState.IsKeyUp(Keys.Right))
{
starSpeed.X += 1.0f;
}
oldKeyState = newKeyState;
starPosition += starSpeed;
ballPosition += ballSpeed;
int MaxX =
graphics.GraphicsDevice.Viewport.Width - texStar.Width;
int MinX = 0;
int MaxY =
graphics.GraphicsDevice.Viewport.Height - texStar.Height;
int MinY = 0;
// Check for ball bounce
if (ballPosition.X > MaxX)
{
ballPosition.X = MaxX;
ballSpeed.X *= -1;
}
if (ballPosition.X < MinX)
{
ballPosition.X = MinX;
ballSpeed.X *= -1;
}
if (ballPosition.Y > MaxY)
{
ballSpeed.Y *= -1;
ballPosition.Y = MaxY;
}
ballSpeed.Y += 1;
// Check for bounce.
if (starPosition.X > MaxX)
{
starSpeed.X *= -1;
starPosition.X = MaxX;
}
else if (starPosition.X < MinX)
{
starSpeed.X *= -1;
starPosition.X = MinX;
}
if (starPosition.Y > MaxY)
{
starSpeed.Y *= -1;
starPosition.Y = MaxY;
}
else if (starPosition.Y < MinY)
{
starSpeed.Y *= -1;
starPosition.Y = MinY;
}
}
else
{
starSpeed=Vector2.Zero;
ballSpeed=Vector2.Zero;
}
}
private Boolean collisionDetected()
{
if (((Math.Abs(starPosition.X - ballPosition.X) < texStar.Width) && (Math.Abs(starPosition.Y - ballPosition.Y) < texBall.Height)))
{
return true;
}
else
{
return false;
}
}
/// <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)
{
graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
// Draw the sprite.
spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend);
spriteBatch.Draw(texStar, starPosition, Color.White);
spriteBatch.Draw(texBall, ballPosition, Color.White);
spriteBatch.End();
base.Draw(gameTime);
}
}
}
A> Marks where the new SpriteObject ballSprite, starSprite; are declared without any problems.
B> Marks where errors arise as I try to use the SpriteObject constructor to create and assign a value to my previously declared SpriteObjects - the errors as per the title of the post. Two such for the first line and one for the second.
The namespace hints at my level of experience with C#. If you could help me understand my almost certainly basic mistake I'd be very grateful.
Those two lines of code you indicated are missing the 'new' operator, which is required to instantiate the Vector2 (or any) class.
ballSprite = new SpriteObject(texBall, new Vector2(200, 0), new Vector2(10.0f, 0.0f));
starSprite = new SpriteObject(texStar, Vector2.Zero, new Vector2(10.0f, 10.0f));
Okay so I'm making a space invader type game in visual studio 2010 c# with xna studio, currently stuck on the bullet class it spawns the bullet but doesn't fire it off in the direction that the sprite is pointing. The bullet class says the Public Vector2 origin; isn't being assigned to anything & is remaining at its default value however I don't know what could be causing it.
Code for bullet
class Bullets
{
public Texture2D texture;
public Vector2 position;
public Vector2 velocity;
public Vector2 origin;
public bool isVisible;
public Bullets(Texture2D newTexture)
{
texture = newTexture;
isVisible = false;
}
public void Draw(SpriteBatch spriteBatch)
{
spriteBatch.Draw(texture, position, null, Color.White, 0f, origin, 1f, SpriteEffects.None, 1);
}
}
Code for the game is:
namespace Rotationgame
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
Vector2 spriteVelocity;
const float tangentialVelocity = 0f;
float friction = 1f;
Texture2D spriteTexture;
Rectangle spriteRectangle;
Rectangle screenRectangle;
// The centre of the image
Vector2 spriteOrigin;
Vector2 spritePosition;
float rotation;
// Background
Texture2D backgroundTexture;
Rectangle backgroundRectangle;
// Shield
Texture2D shieldTexture;
Rectangle shieldRectangle;
// Bullets
List<Bullets> bullets = new List<Bullets>();
KeyboardState pastKey;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
graphics.PreferredBackBufferWidth = 1250;
graphics.PreferredBackBufferHeight = 930;
screenRectangle = new Rectangle(
0,
0,
graphics.PreferredBackBufferWidth,
graphics.PreferredBackBufferHeight);
}
/// <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
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);
shieldTexture = Content.Load<Texture2D>("Shield");
shieldRectangle = new Rectangle(517, 345, 250, 220);
spriteTexture = Content.Load<Texture2D>("PlayerShipup");
spritePosition = new Vector2(640, 450);
backgroundTexture = Content.Load<Texture2D>("Background");
backgroundRectangle = new Rectangle(0, 0, 1250, 930);
// 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 (Keyboard.GetState().IsKeyDown(Keys.Escape))
this.Exit();
// TODO: Add your update logic here
if (Keyboard.GetState().IsKeyDown(Keys.Space) && pastKey.IsKeyUp(Keys.Space))
Shoot();
pastKey = Keyboard.GetState();
spritePosition = spriteVelocity + spritePosition;
spriteRectangle = new Rectangle((int)spritePosition.X, (int)spritePosition.Y,
spriteTexture.Width, spriteTexture.Height);
spriteOrigin = new Vector2(spriteRectangle.Width / 2, spriteRectangle.Height / 2);
if (Keyboard.GetState().IsKeyDown(Keys.Right)) rotation += 0.025f;
if (Keyboard.GetState().IsKeyDown(Keys.Left)) rotation -= 0.025f;
if (Keyboard.GetState().IsKeyDown(Keys.Up))
{
spriteVelocity.X = (float)Math.Cos(rotation) * tangentialVelocity;
spriteVelocity.Y = (float)Math.Sin(rotation) * tangentialVelocity;
}
else if (Vector2.Zero != spriteVelocity)
{
float i = spriteVelocity.X;
float j = spriteVelocity.Y;
spriteVelocity.X = i -= friction * i;
spriteVelocity.Y = j -= friction * j;
base.Update(gameTime);
}
}
public void UpdateBullets()
{
foreach (Bullets bullet in bullets)
{
bullet.position += bullet.velocity;
if (Vector2.Distance(bullet.position, spritePosition) > 100)
bullet.isVisible = false;
}
for (int i = 0; i < bullets.Count; i++)
{
if(!bullets[i].isVisible)
{
bullets.RemoveAt(i);
i--;
}
}
}
public void Shoot()
{
Bullets newBullet = new Bullets(Content.Load<Texture2D>("ball"));
newBullet.velocity = new Vector2((float)Math.Cos(rotation),(float)Math.Sin(rotation)) * 5f + spriteVelocity;
newBullet.position = spritePosition + newBullet.velocity * 5;
newBullet.isVisible = true;
if(bullets.Count() < 20)
bullets.Add(newBullet);
}
/// <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();
spriteBatch.Draw(backgroundTexture, backgroundRectangle, Color.White);
spriteBatch.Draw(shieldTexture, shieldRectangle, Color.White);
foreach (Bullets bullet in bullets)
bullet.Draw(spriteBatch);
spriteBatch.Draw(spriteTexture, spritePosition, null, Color.White, rotation, spriteOrigin, 1f, SpriteEffects.None, 0);
spriteBatch.End();
// TODO: Add your drawing code here
base.Draw(gameTime);
}
}
Where am I going wrong?
When you draw the bullet you pass origin as an argument but never set a value to it. You should find the point on the image which you want to be the origin (so if you place the bullet at 0,0 that part of the image will be on 0,0).
If you don't set a value for a variable and then try and pass it into a function if it's needed by the function then this will cause the program to crash.
Try 0,0 first so in the class
Vector2 origin=new Vector2(0,0);
That should make your code work, tweak the origin based on your preferences.
Also instead of making these variables public it is far better practise to make the private and create get and set functions in bullet to set them or get the values. This minimises the risk of unpredictable access and modification
EDIT: looked again realised that although a problem was not THE problem.
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (Keyboard.GetState().IsKeyDown(Keys.Escape))
this.Exit();
updateBullets();
...
}
You need to actually call updateBullets in your update function, it won't get called automatically
I modified your Game classes Update method to call your UpdateBullets method and now the bullet sprites will travel in a direction and then disappear.
I have also fixed the Shoot() method to properly direct the bullets according to the direction the ship is rotated to.
protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (Keyboard.GetState().IsKeyDown(Keys.Escape))
this.Exit();
// TODO: Add your update logic here
if (Keyboard.GetState().IsKeyDown(Keys.Space) && pastKey.IsKeyUp(Keys.Space))
Shoot();
pastKey = Keyboard.GetState();
spritePosition = spriteVelocity + spritePosition;
spriteRectangle = new Rectangle((int)spritePosition.X, (int)spritePosition.Y,
spriteTexture.Width, spriteTexture.Height);
spriteOrigin = new Vector2(spriteRectangle.Width / 2, spriteRectangle.Height / 2);
if (Keyboard.GetState().IsKeyDown(Keys.Right)) rotation += 0.025f;
if (Keyboard.GetState().IsKeyDown(Keys.Left)) rotation -= 0.025f;
if (Keyboard.GetState().IsKeyDown(Keys.Up))
{
spriteVelocity.X = (float)Math.Cos(rotation) * tangentialVelocity;
spriteVelocity.Y = (float)Math.Sin(rotation) * tangentialVelocity;
}
else if (Vector2.Zero != spriteVelocity)
{
float i = spriteVelocity.X;
float j = spriteVelocity.Y;
spriteVelocity.X = i -= friction * i;
spriteVelocity.Y = j -= friction * j;
base.Update(gameTime);
}
UpdateBullets();
}
public void Shoot()
{
Bullets newBullet = new Bullets(Content.Load<Texture2D>("ball"));
newBullet.velocity = new Vector2((float)Math.Sin(rotation), (float)Math.Cos(rotation)) * new Vector2(5f,-5f) + spriteVelocity;
newBullet.position = spritePosition + newBullet.velocity * 5;
newBullet.isVisible = true;
if (bullets.Count < 20)
bullets.Add(newBullet);
}
I am following: http://xbox.create.msdn.com/en-US/education/tutorial/2dgame/animating_the_player and it was when during implementing the animation something went wrong. But I am not sure why:
Game1.cs (the only method I changed here was LoadContent()-method and UpdatePlayer()-method.
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.Input.Touch;
namespace Shooter
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
//Represents the player
Player player;
KeyboardState currentKeyboardState;
KeyboardState previousKeyboardState;
GamePadState currentGamePadState;
GamePadState previousGamePadState;
float playerMoveSpeed;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
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
player = new Player();
playerMoveSpeed = 8.0f;
TouchPanel.EnabledGestures = GestureType.FreeDrag;
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);
// TODO: use this.Content to load your game content here
//Load the player resources
// Load the player resources
Animation playerAnimation = new Animation();
Texture2D playerTexture = Content.Load<Texture2D>("shipAnimation");
playerAnimation.Initialize(playerTexture, Vector2.Zero, 115, 69, 8, 30, Color.White, 1f, true);
Vector2 playerPosition = new Vector2(GraphicsDevice.Viewport.TitleSafeArea.X, GraphicsDevice.Viewport.TitleSafeArea.Y
+ GraphicsDevice.Viewport.TitleSafeArea.Height / 2);
player.Initialize(playerAnimation, playerPosition);
}
/// <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();
previousGamePadState = currentGamePadState;
previousKeyboardState = currentKeyboardState;
currentKeyboardState = Keyboard.GetState();
currentGamePadState = GamePad.GetState(PlayerIndex.One);
UpdatePlayer(gameTime);
base.Update(gameTime);
}
private void UpdatePlayer(GameTime gameTime)
{
player.Update(gameTime);
player.Position.X += currentGamePadState.ThumbSticks.Left.X *playerMoveSpeed;
player.Position.Y -= currentGamePadState.ThumbSticks.Left.Y *playerMoveSpeed;
if (currentKeyboardState.IsKeyDown(Keys.Left) || currentGamePadState.DPad.Left == ButtonState.Pressed)
{
player.Position.X -= playerMoveSpeed;
}
if (currentKeyboardState.IsKeyDown(Keys.Right) || currentGamePadState.DPad.Right == ButtonState.Pressed)
{
player.Position.X += playerMoveSpeed;
}
if (currentKeyboardState.IsKeyDown(Keys.Up) || currentGamePadState.DPad.Up == ButtonState.Pressed)
{
player.Position.Y -= playerMoveSpeed;
}
if (currentKeyboardState.IsKeyDown(Keys.Down) || currentGamePadState.DPad.Down == ButtonState.Pressed)
{
player.Position.Y += playerMoveSpeed;
}
//Make sure the player does not go out of bounds
player.Position.X = MathHelper.Clamp(player.Position.X, 0, GraphicsDevice.Viewport.Width - player.Width);
player.Position.Y = MathHelper.Clamp(player.Position.Y, 0, GraphicsDevice.Viewport.Height - player.Height);
}
/// <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();
player.Draw(spriteBatch);
spriteBatch.End();
base.Draw(gameTime);
}
}
}
Player.cs
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
namespace Shooter
{
class Player
{
//Animation representing the player
public Animation PlayerAnimation;
//Position of the Player relative to the upper left side of the screen
public Vector2 Position;
//State of the player
public bool Active;
//Amount of hit points that player has
public int Health;
//Get the width of the player ship
public int Width
{
get { return PlayerAnimation.FrameWidth; }
}
//Get the height of the player ship
public int Height
{
get { return PlayerAnimation.FrameHeight; }
}
public void Initialize(Animation animation, Vector2 position)
{
PlayerAnimation = animation;
//Set the starting position of the player around the middle of the screen and to the back
Position = position;
//Set the player to be active
Active = true;
//Set the player health
Health = 100;
}
public void Update(GameTime gameTime)
{
PlayerAnimation.Position = Position;
PlayerAnimation.Update(gameTime);
}
public void Draw(SpriteBatch spriteBatch)
{
PlayerAnimation.Draw(spriteBatch);
}
}
}
Animation.cs
using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
namespace Shooter
{
class Animation
{
// The image representing the collection of images used for animation
Texture2D spriteStrip;
// The scale used to display the sprite strip
float scale;
// The time since we last updated the frame
int elapsedTime;
// The time we display a frame until the next one
int frameTime;
// The number of frames that the animation contains
int frameCount;
// The index of the current frame we are displaying
int currentFrame;
// The color of the frame we will be displaying
Color color;
// The area of the image strip we want to display
Rectangle sourceRect = new Rectangle();
// The area where we want to display the image strip in the game
Rectangle destinationRect = new Rectangle();
// Width of a given frame
public int FrameWidth;
// Height of a given frame
public int FrameHeight;
// The state of the Animation
public bool Active;
// Determines if the animation will keep playing or deactivate after one run
public bool Looping;
// Width of a given frame
public Vector2 Position;
public void Initialize(Texture2D texture, Vector2 position,
int frameWidth, int frameHeight, int frameCount,
int frametime, Color color, float scale, bool looping)
{
// Keep a local copy of the values passed in
this.color = color;
this.FrameWidth = frameWidth;
this.FrameHeight = frameHeight;
this.frameCount = frameCount;
this.frameTime = frametime;
this.scale = scale;
Looping = looping;
Position = position;
spriteStrip = texture;
// Set the time to zero
elapsedTime = 0;
currentFrame = 0;
// Set the Animation to active by default
Active = true;
}
public void Update(GameTime gameTime)
{
// Do not update the game if we are not active
if (Active == false)
return;
// Update the elapsed time
elapsedTime += (int)gameTime.ElapsedGameTime.TotalMilliseconds;
// If the elapsed time is larger than the frame time
// we need to switch frames
if (elapsedTime > frameTime)
{
// Move to the next frame
currentFrame++;
// If the currentFrame is equal to frameCount reset currentFrame to zero
if (currentFrame == frameCount)
{
currentFrame = 0;
// If we are not looping deactivate the animation
if (Looping == false)
Active = false;
}
// Reset the elapsed time to zero
elapsedTime = 0;
}
// Grab the correct frame in the image strip by multiplying the currentFrame index by the frame width
sourceRect = new Rectangle(currentFrame * FrameWidth, 0, FrameWidth, FrameHeight);
// Grab the correct frame in the image strip by multiplying the currentFrame index by the frame width
destinationRect = new Rectangle((int)Position.X - (int)(FrameWidth * scale) / 2,
(int)Position.Y - (int)(FrameHeight * scale) / 2,
(int)(FrameWidth * scale),
(int)(FrameHeight * scale));
}
public void Draw(SpriteBatch spriteBatch)
{
if (Active)
{
spriteBatch.Draw(spriteStrip, destinationRect, sourceRect, color);
}
}
}
}
In Game1.cs this is what checking the boundaries. However, this was not changed in this part of the tutorial, so I am not sure why it started to happen, have I missed something? Anything you guys can point out that I did wrong?
//Make sure the player does not go out of bounds
player.Position.X = MathHelper.Clamp(player.Position.X, 0, GraphicsDevice.Viewport.Width - player.Width);
player.Position.Y = MathHelper.Clamp(player.Position.Y, 0, GraphicsDevice.Viewport.Height - player.Height);
After looking at your code, My best guess is that the problem is with the draw function of your animation and how destinationRect is defined.
It looks like the game thinks that the center of your Animation is the top-left corner of your animation..
I think maybe you want to set destinationRect in the Update method of your animation like so
// Grab the correct frame in the image strip by multiplying the currentFrame index by the frame width
destinationRect = new Rectangle(
(int)Position.X,
(int)Position.Y,
(int)(FrameWidth * scale),
(int)(FrameHeight * scale));
with the Rectangle's X and Y coordinates being the top left corner of your animation instead of the center.
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; }
}
}
}
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();
}
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.