Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

C# Simple 2D Game Physics (Gravity, Jumping, Movement & Block Collision)

0.00/5 (No votes)
28 Feb 2015 2  
How I made my 2D Game, a Mario platformer type of game. I searched around the internet for most of these things, but had trouble finding it. Especially collision detection that I ended up creating a simple solution by myself.

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.

C#
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.

C#
int Force
int Gravity
Boolean Player_Jump

Here's how the Space keyDown is set up:

C#
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:

C#
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:

C#
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:

C#
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.

C#
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:

C#
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:

C#
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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here