Introduction
This is work by a simple guy doing it for fun.
When I made my first 2D game in C#, I had some problems with adding new blocks also getting working collision physics so the player wouldn't fall through the block or get stuck.
That's when I searched around the cyberweb for some pointers, or any working code that I could learn from. But I never found any that isn't using the XNA framework. I find XNA pretty aids to work with.
So I made my own, a simple collision detection that I would like to explain for others like myself.
Here's an image of the game I made.
Explanation of the game
You're Mario, going against 2 Goombas following you and falling bombs as well as that Rockets are fired at you from the sides. (Not shown in the image however)
There's 2 platforms, 1 Pipe (doesn't let you go through it :P)
Whilst everything is happening, your goal is to collect as many coins as possible (falling from the sky randomly) and staying alive as long as possible.
Keyboard Input
The keyboard input, where I move the character with Arrow keys (or (WASD) were used with the forms KeyDown
/KeyUp
event.
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.Left:
Player_Left = true;
break;
case Keys.Right:
Player_Right = true;
break;
}
}
Just an example how I did mine, as you can read there's a switch on which keypress is made and from there it does something depending on the key.
If I press left arrow key, it changes my "Move player Left" variable to true
.
Gravity
For Gravity
in this game, I was using a Timer that moves the character up and down depending on the following variables.
int Force
int Gravity
Boolean Player_Jump
Here's how the Space keyDown
is set up:
if (!Player_Jump && !InAirNoCollision(pb_Player))
{
Force = Gravity;
Player_Jump = true;
}
What this says is that, if the Player
is not already jumping and the player is NOT in the air as well as NOT colliding with any block (also checks so the player is within the worldframe).
My physics in this game works in the way that if there is any value in Force, the player is moved upwards (unless head is colliding with a block).
If the Force
is down at 0
, the player starts falling.
I made this with 2 timers (Can be done with 1, I like to sort them aside however).
Here's how I did the falling code:
private void timer_Gravity_Tick(object sender, EventArgs e)
{
if (!Player_Jump && pb_Player.Location.Y +
pb_Player.Height < WorldFrame.Height - 2 && !Collision_Top(pb_Player))
{
pb_Player.Top += Speed_Fall;
}
if (!Player_Jump && pb_Player.Location.Y + pb_Player.Height > WorldFrame.Height - 1)
{
pb_Player.Top--;
}
}
And this is the jumping code:
private void timer_Jump_Tick(object sender, EventArgs e)
{
if (Force > 0)
{
if (Collision_Bottom(pb_Player))
{
Force = 0;
}
else
{
Force--;
pb_Player.Top -= Speed_Jump;
}
}
else
{
Player_Jump = false;
}
}
Movement
Like in my example in the Keyboard input section, I use the following code to start moving:
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.Left:
Player_Left = true;
break;
case Keys.Right:
Player_Right = true;
break;
}
}
A Boolean for Player_Left, Player_Right
I also have a timer that moves the character depending on whether the Boolean is true
or not.
if (Player_Right && pb_Player.Right <= WorldFrame.Width - 3 && !Collision_Left(pb_Player))
{
pb_Player.Left += Speed_Movement;
}
if (Player_Left && pb_Player.Location.X >= 3 && !Collision_Right(pb_Player))
{
pb_Player.Left -= Speed_Movement;
}
}
else
{
Player_Right = false;
Player_Left = false;
}
This is just an example of how I use the code, like the Speed_movement
is just an Int
set to in my case 3
.
Block Collision
In my code, I'm using PictureBoxes
as the Player
as well as WorldObjects
.
In this example, let's just have the following variables:
PictureBox Player
PictureBox WorldObjects Block_1
I use 4 Boolean functions, to check if the Player is intersecting with the blocks sides, but to achieve this, I had to create some temporary rectangle on each side of the Block.
For example, top collision:
PictureBox temp1 = new PictureBox();
temp1.Bounds = Block_1.Bounds;
temp1.SetBounds(temp1.Location.X, temp1.Location.Y - 1, temp1.Width, 1);
- Line #1 What this does is that it creates a new picturebox
- Line #2 Copies the bounds (The size of the source block, this case
Block_1
) - Line #3 Sets the new bounds, at the same X axis, but 1 point above
Block_1
and just as wide as Block_1
See "Using the code" section for the entire code, or download the source and try it out yourself.
Using the Code
This is the InAirNoCollision
code as well as all the 4 Collision
functions:
public Boolean InAirNoCollision(PictureBox tar)
{
if (!OutsideWorldFrame(tar))
{
foreach (PictureBox Obj in WorldObjects)
{
if (!tar.Bounds.IntersectsWith(Obj.Bounds))
{
if (tar.Location.Y < WorldFrame.Width)
{
return true;
}
}
}
}
return false;
}
public Boolean Collision_Top(PictureBox tar)
{
foreach (PictureBox ob in WorldObjects)
{
if (ob != null)
{
PictureBox temp1 = new PictureBox();
temp1.Bounds = ob.Bounds;
temp1.SetBounds(temp1.Location.X - 3, temp1.Location.Y - 1, temp1.Width + 6, 1);
if (tar.Bounds.IntersectsWith(temp1.Bounds))
return true;
}
}
return false;
}
public Boolean Collision_Bottom(PictureBox tar)
{
foreach (PictureBox ob in WorldObjects)
{
if (ob != null)
{
PictureBox temp1 = new PictureBox();
temp1.Bounds = ob.Bounds;
temp1.SetBounds(temp1.Location.X, temp1.Location.Y + temp1.Height, temp1.Width, 1);
if (tar.Bounds.IntersectsWith(temp1.Bounds))
return true;
}
}
return false;
}
public Boolean Collision_Left(PictureBox tar)
{
foreach (PictureBox ob in WorldObjects)
{
if (ob != null)
{
PictureBox temp1 = new PictureBox();
temp1.Bounds = ob.Bounds;
temp1.SetBounds(temp1.Location.X - 1, temp1.Location.Y + 1, 1, temp1.Height - 1);
if (tar.Bounds.IntersectsWith(temp1.Bounds))
return true;
}
}
return false;
}
public Boolean Collision_Right(PictureBox tar)
{
foreach (PictureBox ob in WorldObjects)
{
if (ob != null)
{
PictureBox temp1 = new PictureBox();
temp1.Bounds = ob.Bounds;
temp1.SetBounds(temp1.Location.X + temp1.Width, temp1.Location.Y + 1, 1, temp2.Height - 1);
if (tar.Bounds.IntersectsWith(temp2.Bounds))
return true;
}
}
return false;
}
I've uploaded a .zip file containing an executable file, resource files (all the images), and a full documented source code.
If you have any questions, feel free to email me at robert@hydeen.se.