This is my first attempt at a game, it's already been quite the learning experience thusfar but I've now grinded to a halt when trying to figure out how to do my collision detection on a level-wide scale.
I started with the basics like a simple player class, an obstacle class for blocks to jump on, then my movement & jumping which went smooth, then collision with 1 block.
Now I'm drawing a level on my screen with a simple array and a for loop, but I'm lost on how to proceed to apply my collision level-wide. The way I've attempted it is the best I could come up with myself, running a separate loop with the same array and then running my collision logic. I've looked at tutorials online and the platformer example Microsoft provides in their XNA resources but the way their collision systems are written always seems much more complex and with many more factors such as a more detailed physics system and more complicated level drawing, which would require me to basically start over. Which is no problem if that's required and my foundation is simply not good, but I'd like to know if I can continue with what I have.
Edit: I forgot to mention what actually goes wrong. Basically applying my collision logic to my level like this results in two huge bugs, one where my character slowly sinks through the blocks he stands on, another where at certain angles of collision with a block he will teleport to the nearest block. Both these issues did not exist when I applied the same collision to a single block. I've tried playing with the values to push my player away with different methods but with no success. I really just can't figure out what the issue actually is.
Here is my class handling level drawing and collision:
namespace GameDevProtoType
{
public class Level
{
Vector2 obstaclePosition;
protected byte[,] tileArray;
Obstacle obstacle;
Rectangle obstacleBounds;
protected void CreateTileArray()
{
tileArray = new byte[,]
{
{ 0,0,0,0,0,0,0,0,0 },
{ 0,0,0,0,1,0,0,0,0 },
{ 0,0,0,0,0,0,0,0,0 },
{ 0,0,0,1,0,0,0,0,0 },
{ 0,0,0,0,0,0,0,0,0 },
{ 0,0,0,1,1,0,0,0,0 },
{ 1,1,1,1,1,0,0,0,0 },
{ 0,0,0,0,0,0,0,0,0 },
{ 0,0,0,0,0,0,0,0,0 },
{ 0,0,1,0,0,0,0,0,0 },
{ 1,1,1,1,0,0,0,0,0 },
{ 0,0,0,0,0,0,0,1,1 },
{ 0,0,0,1,1,0,1,0,0 },
{ 0,0,1,0,0,0,0,0,1 }
};
}
public void Initialize()
{
obstacle = new Obstacle();
}
public void CreateLevel(ContentManager content, SpriteBatch spritebatch)
{
CreateTileArray();
Texture2D obstacleSprite = content.Load<Texture2D>("Graphics\\Tile1");
obstacle.Initialize(obstacleSprite);
for (int row = 0; row < tileArray.GetLength(0); row++)
{
for (int column = 0; column < tileArray.GetLength(1); column++)
{
if (tileArray[row, column] == 1)
{
obstaclePosition = new Vector2(column * obstacle.Width, row * obstacle.Height);
spritebatch.Draw(obstacleSprite, obstaclePosition, null, Color.White, 0f, Vector2.Zero, 1f, SpriteEffects.None, 0f);
}
}
}
}
public void Collision(GameTime gameTime, Rectangle playerBounds, Player player, Vector2 playerMoveSpeed)
{
CreateTileArray();
for (int row = 0; row < tileArray.GetLength(0); row++)
{
for (int column = 0; column < tileArray.GetLength(1); column++)
{
if (tileArray[row, column] == 1)
{
obstaclePosition = new Vector2(column * 32, row * 32);
obstacleBounds = new Rectangle((int)(obstaclePosition.X), (int)(obstaclePosition.Y), 32, 32);
if (playerBounds.Intersects(obstacleBounds))
{
float rightEdge = obstacleBounds.X - (player.Position.X + playerBounds.Width);
float leftEdge = obstacleBounds.X + obstacleBounds.Width - player.Position.X;
float topEdge = obstacleBounds.Y - (player.Position.Y + playerBounds.Height);
float bottomEdge = obstacleBounds.Y + obstacleBounds.Height - player.Position.Y;
float shortestWidth = Math.Min(Math.Abs(rightEdge), Math.Abs(leftEdge));
float shortestHeight = Math.Min(Math.Abs(topEdge), Math.Abs(bottomEdge));
float shortestFinal = Math.Min(Math.Abs(shortestWidth), Math.Abs(shortestHeight));
if (shortestFinal == Math.Abs(leftEdge))
{
player.Position.X = obstacleBounds.X + obstacleBounds.Width;
playerMoveSpeed.X = 0;
}
else if (shortestFinal == Math.Abs(rightEdge))
{
player.Position.X = obstacleBounds.X - playerBounds.Width;
playerMoveSpeed.X = 0;
}
else if (shortestFinal == Math.Abs(bottomEdge))
{
player.Position.Y = obstacleBounds.Y + obstacleBounds.Height;
playerMoveSpeed.Y = 0;
}
else if (shortestFinal == Math.Abs(topEdge))
{
player.Position.Y = obstacleBounds.Y - playerBounds.Height;
playerMoveSpeed.Y = 0;
player.hasJumped = false;
}
}
}
}
}
}
}
}
Here is how I call my collision logic (and update playerbounds which is handled in my game logic) from main game class:
protected override void Update(GameTime gameTime)
{
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed || Keyboard.GetState().IsKeyDown(Keys.Escape))
Exit();
currentKeyboardState = Keyboard.GetState();
UserInput(gameTime);
level.Collision(gameTime, playerBounds, player, playerMoveSpeed);
playerBounds.X = (int)player.Position.X;
playerBounds.Y = (int)player.Position.Y;
base.Update(gameTime);
}
CreateLevel is called in the final method Draw().
edit: apologies for some bad programming etiquette, i was focusing on making collision work and go from there and took some shortcuts as you can see