Basic Character Collision

Before I get into the tutorial, I just want to talk a bit about why I am writing it and how it is different than many other tutorials. I have been using online tutorials myself for over a decade and while I did learn from them, they never seemed to explain what it was they were doing. Many just walk you step by step of the example and the reader should be able to recreate it, but often, that was it. I always wanted to know why I did something, what each element did, etc. Basically, most tutorials are instructions and when what you really want is the process. Hopefully, I will show you the thought process that goes with the example. Now on to the tutorial.

Basic Character Collision

This tutorial is scripted in Game Maker 8.0, but the concepts should be easily translated into any coding software.

I have noticed that many students get frustrated that they “don’t know the functions” they need to create what they want, which is often incorrect. They actually do know all the code they will need, they just haven’t thought through what it is that are attempting to do. There is no shame in this, as honestly, we all do it and we all feel dumb when we realize it. Therefore, before we dive into the code, let take a look a what we are going to be building here.

The Design

We are going to focus on a very basic collision system for a platforming game. This will involve one character moving around a world with solid walls and ground. That sounds pretty straight forward, but this is usually were the trouble begins. We are missing plenty of information that we need to build this correctly. Here is what you need to be thinking about:

  • Character must move left and right
  • Character must jump
  • Gravity should send player down towards ground
  • Player should stop going down when touching ground
  • Player should stop when hitting a wall
  • Player should still move down if against a wall but not on the ground
  • Player should hit platform above
  • If player lets go of controls and is on the ground, friction should apply and come to a stop

Now that is a good breakdown.

The Implementation

Work File: BasicCollision.gmk

Above I have a very basic file which you can use with the objects and room already built. However, if you want to build your own, this is what you will need to do. There three objects, all of which are 32×32 pixels and this is just for simplicity. The size only affects the distances we use, and even that, is only a visual thing. It will still function correctly. All the sprites you create should have precision checking turned off. This is very important, as otherwise you are checking for every pixel in the object, which means that things will get stuck. Finally, the wall and platform objects are set to be solid, while the character is not. You should never make a moving object solid as Game Maker treats collisions with solid objects differently from non-solid ones.

Now we are ready to get into Game Maker and start coding this system. Most people use the built-in Collision Event, but we will not be doing this. The collision event, while useful, doesn’t do everything we need it to. This comes down to how the Event system works within the program.

Game Maker has a very specific order in which the events are run. Some events, such as Create, Destroy, etc happen immediately when called. The rest happen in this order:

  • Begin step events
  • Alarm events
  • Keyboard, Key press, and Key release events
  • Mouse events
  • Normal step eventsย (now all instances are set to their new positions)
  • Collision events
  • End step events
  • Draw events

As you can see, the collision event happens near the end of the chain. Likely you have applied the movement of objects to the Keyboard or Step Events in the past. That means the character may never collide with the object or end up inside it. Therefore we need to be in control of when everything happens. For our example, we will be using four events: Create, Begin Step, Step, and End Step. These represent the four stages of movement: Initialization, Ability to Move, Moving, and Correcting.

Stage 1 – Initialization

The first stage is really only to set up the variable we will be needing to run the system. There are four variables for the collision and one which we will use to set the horizontal speed. The following code goes in the Create Event of the character.

//Collison Variables
onGround = 0;
onCeiling = 0;
left = 0;
right = 0;

//Default horizontal speed
hspd = 8;

Now, you might be wondering why I have the collision variables being set to 0 rather than false. The main reason I am doing this is because I want to be able to double check my work in the correction stage. If I had only true or false, I could only confirm that it has or has not done something. Instead, I will use 0,1, and 2, which will essentially mean: false, true, and was and is still true. I find that doing this minimizes errors in general.

Stage 2 – Ability to Move

Before we move the character, we need to see if there are any obstructions. This requires that we forecast what is about to happen. We first need to see if we are on the ground or not. If we are NOT, then we should be falling during this frame. We also need to see if we are close to the ground, in which case, we should snap to the surface. We also need to check to see if we can move to the left and the right. Finally, if we are on the ground, it means we are capable of jumping, so we should also check to see if we are obstructed above the character.

The following code should be put into a script and placed in the Begin Step Event of the character.

//Ground Check
if !place_free(x,y+1) {
  onGround = 1; vspeed = 0;
} else {
  onGround = 0;
}

//Ceiling Check
if !place_free(x,y-8) {
  onCeiling = 1; vspeed = 8;
} else {
  onCeiling = 0;
}

//Check collision to the right

if place_free(x+hspd,y) {
  right = 1;
} else {
  right = 0; hspeed = 0; move_contact_solid(0,hspd);
}

//Check collision to the left
if place_free(x-hspd,y) {
  left = 1;
} else {
  left = 0; hspeed = 0; move_contact_solid(180,hspd);
}

One thing you need to notice here is that I am asking two different questions here; One for vertical collision and another for horizontal. At first, this may seem an odd way of doing things. The thought process here is that it is really all about when the character can do something or not.

The player is only able to jump if they are on the ground, therefore, we check to see if the space is NOT empty below it ( !place_free ). The player is able to move to the left or right so long as they are not obstructed by anything, hence the check for empty space ( place_free ). You can also see that I am changing the speed (hspeed or vspeed) in different places. This is for the same reason. If the player is on the ground, we don’t want it to continue downward. If it hits the ceiling, then we want it to go down. If there is collision to the left or right, then we want it to stop as well.

Another question you may have is why I chose to use place_free in the first place, rather than the many other options available, such as place_empty or position_empty. The main reason is that place_free only looks for objects with collision, while the other two look for any object. They would technically work in our example, but it would very quickly become limiting.

Stage 3 – Movement

Now that we have some answers for what our character can do, we can actually move it. We need to do a check to see if the button has been pressed and if we are able to move in that direction. We need a gravity check to see if we are currently falling , which will increase the downward speed. We also need to cap the vertical speed so it doesn’t drop too fast. Finally, we add friction to the character if they are on the ground but the direction keys are not pressed. This should go in the Step Event.

if keyboard_check(vk_right) && right == 1 { hspeed = hspd; right = 2;}
if keyboard_check(vk_left) && left == 1 { hspeed = -hspd; left = 2;}
if keyboard_check(vk_up) && onGround == 1 && onCeiling == 0 { vspeed = -10;}

//Gravity
if onGround == 0 and vspeed < 8 { vspeed += 0.5;}
if vspeed > 8 { vspeed = 8;}

//Friction
if onGround == 1 {
if !keyboard_check(vk_right) { !keyboard_check(vk_left) {
if right == 1 && hspeed > 0 { hspeed -= 0.5; right = 2;}
if left == 1 && hspeed < 0 { hspeed += 0.5; left = 2;}
}
}

Here you can see that if we are able to move and the input is given, we will move as directed. You should also notice that the left and right have both been set to 2, as we will want to double check this soon. This occurs whether we are holding the controls down or not.

Stage 4 – Correcting

Once the character has moved, we need to make sure it isn’t inside of an object or if it is close to a wall or the ground. In the End Step, we will check to see where the character now is and move the character to the surface if it is close. This should go in the End Step Event.

if !place_free(x+1,y) { hspeed = 0; move_contact_solid(0,hspd);}
if !place_free(x-1,y) { hspeed = 0; move_contact_solid(180,hspd);}
if !place_free(x,y+8) { vspeed = 0; move_contact_solid(270,8);}
if !place_free(x,y-8) && vspeed < 0 { vspeed = 0; move_contact_solid(90,8);}

Here we are using move_contact_solid which is super useful in this case. It allows us to snap to the surface of a solid object within a certain distance. Without this, you will often see that the character will not align with the walls / floors as the folling step it might be too close to make the check true. Better safe than sorry, right!

Conclusion

The Collision System we just built is fairly good for prototyping purposes, but it is definitely not perfect. You may notice a slight snapping of the character when slowing down near a wall. Also, if you move around the world and jump into a platform on a 45 degree angle, you will likely see that you can get stuck. This is due to the fact that we are not checking the angles at all, only up/down/left/right. It gets stuck because in the Begin Step, the space above and to the sides are all empty. In the future I will cover these topics. Otherwise I recommend the GMC forums as there are many scripts you can download to check out there code.

Keep making games!

4 thoughts on “Basic Character Collision

  1. “they never seemed to explain what it was they were doing”

    If your goal was to go beyond that and explain what the scripts are doing and how they work, it is my opinion you failed. Please don’t hurt me ๐Ÿ˜›

    Example: In your second script, the first if statement does a check to see if the “player” is on the ground, and you explain that is the purpose of the script. However you give no details as to how the script works. At first glance I could not figure out why you would set onGround = 1 if place_empty is true. When in fact you are doing that if it is false.

    I would recommend next time walking through each of the scripts a bit more and explaining how they work in more detail. In the above example I would have made a point to explain that you added the “!” in to make it a “not place_empty”.

    More detail like that about how the functions in your script work, and why you are using them over other possible options would help a lot.

    More examples of this is why the hell is right being set to 2? I don’t see it ever being set to 1. Only ever 2 (well and 0 at the beginning), yet you check if its 1…

    Break apart the scripts line by line and explain what each line is doing in more detail. If you include the whole script first, and then break it down line by line the reader can look at the whole script, and any part they do not understand they can read more about bellow in the detailed section.

    Also you end the tutorial with “this is broken.” Might want to consider adding in some recommendations as to where someone can look for solving the problem, mention you have another tutorial coming up to address that, or something. Ending it like that just seems to shout “this sucks why are you reading it?”

    Anyways, just my $0.02

    That all being said, I look forward to your next tutorial.

    Regner

  2. Since there is no edit button, I will submit another comment.

    After reading over the tutorial again I do see where the “right” and “left” variables get set to 1, however that just emphasizes my point I think. With a little more explanation and breaking down it would have been far more clear as to what everything in your script was doing.

  3. I love the layout of the site. Have you been doing this all on your spare time? This is awesome Jason. Great tutorial. I might have to give Game Maker a try sometime ๐Ÿ™‚

  4. Thanks for reading.

    Great feedback Regner. I did say “hopefully” and after a second look, you nailed it on the head. I missed certain important details which could confuse people. In fact, I realized I had made some mistakes. I should not have used place_empty in the first place. It works perfectly fine in this example, but not good for anything more robust. Thanks!

    For any reading this and wondering WTF, this is no longer the exact same article Regner responded to, but the improved version.

    Now that I have fixed it, I see some things I am going to do different next time.

    Your $0.02 has been very well spent ๐Ÿ™‚

Leave a Reply