Today's tech blog takes form of a mini-tutorial, where we will be making a small physics simulation and at the same time we'll discuss some of the issues that people come up against when transitioning from the "traditional" GameMaker Studio 2 approach to movement and collisions to the physics approach.
Even the most advanced users can be confused when using GameMaker Studio 2 physics for the first time, so we'll start this tutorial with probably the most important piece of advice we can give... Forget everything you know about how GameMaker Studio 2 deals with collisions and movement! Why? Well, when you enable physics in a room, you are changing how everything will interact at a very basic level within GameMaker, and this makes the built in variables like
direction obsolete. However, it does enable a whole bunch of specific physics variables which can be used instead.
The other major change that happens when you enable physics is that collisions will be handled automatically for you. With "traditional" GameMaker collisions you would have a Collision Event and then have some code or action in there to deal with what happens, but - with the physics enabled - the collisions will be resolved by GameMaker automatically, with bounce angles and velocities etc... being calculated and applied for you.
So, with that warning out of the way, lets move on and get started making a simple physics simulation... You'll need to make a new GML project in GameMaker Studio 2 and open the Room Editor before continuing.
THE PHYSICS WORLD
To enable physics, you must first set up the physics world. This is done in the room editor, from the physics tab:
Obviously we need to tick the option Enable Physics (otherwise any physics enabled objects placed in the room will cause errors), and then we can set the gravity and the scale for the room. The room Gravity is calculated as a vector, with the distance and the direction of the vector giving the gravity strength in meters per second and the direction of the gravity pull.
The scale option Pixels to Meters isn't quite so obvious. In the physics simulation, all reactions are based off of idealised "real life" physics, and so the scale of the interaction is important. A 1m cube of lead will not react the same as a 1cm cube of lead, and so we need to let the physics simulation know what scale we are working with. So, here you set how many meters one pixel in the game relates to.
For simplicity we'll leave the default values for this tab and you can close the room editor for now. We'll now need to quickly make some sprites to use for this project. You'll need two block sprites (one for the floor and one to be, well, a block), a circle sprite and a triangle sprite, and make sure to center their origins. They could be any size you want, but for the sake of consistency, keep them around the 32x32px size, as that's what was used to make the Demo file available at the end of this article.
We need to define our objects now, starting with a couple of parent objects. These parent objects won't have any physics properties themselves, and will only be used to "group" collisions together for ease of use. Now, as we stated at the start of this article, with physics enabled objects will react to collisions automatically, but we still have to tell the physics simulation which objects we want to collide. So to do that we still need to give them a Collision Event, although we will add no code to it, just a simple comment to tell the physics that these instances should interact.
Make two objects now and call them "
obj_StaticParent" and "
obj_DynamicParent". Then add collision events in both of them checking for collisions with themselves and each other so that they look like this:
You should also add an Outside Room event to the Dynamic parent, and in that put the GML function
instance_destroy() so that if any instance falls out the room it is removed.
Why do we have two parents? Well, in the physics simulation you can have have three basic types of object: static, dynamic and kinematic. Each one has slightly different properties and will behave in a different way:
Static - A static body is one that does not react to any external force, which means that it is unaffected by gravity or collisions and will remain immobile in the position that it is placed within the room unless explicitly moved through code. Think of the "wall" blocks you would place in a top down game, for example - in a physics world they would be classed as static objects.
Dynamic - A dynamic body is one that participates in the full physics simulation and it will react to any forces applied to, like gravity or when in a collision. Anything that moves and reacts to other instances in a game would normally be dynamic.
Kinematic - A kinematic body is a sort of mix between static and dynamic. It is an instance that has been flagged as static, but has then been set in motion through code by setting it's speed directly (not through forces). If you think of a top down game again, then you could make any doors that slide open or closed kinematic so that they don't rotate or react to forces, but they do still move.
In this basic project, we won't be using kinematic objects, but we will need static and dynamic ones, which is why we have the two parent objects - it just simplifies collision checking a lot!
We now need to add four more physics enabled objects: one that is static for the "floor", and three dynamic ones, so go ahead and create these four objects now and give each one an appropriate sprite from those we made earlier:
You need to assign the floor object to have the
obj_StaticParent as its parent, and the other three objects should be children of
obj_DynamicParent to ensure that the collisions are resolved correctly. We can now move on and define the fixtures for the objects.
Okay, we have our physics enabled room and our objects, but we haven't actually given our objects any physical properties yet, have we? We need to tell GameMaker Studio 2 what properties the physics body will have, which will determine how it interacts with other physics bodies in the game. This is done by defining the fixture.
You define a fixture in two parts, the first being the shape it should have and the second being the properties it the shape should have. We can do this directly from the object editor itself or from code, but either way what happens is you define the fixture and then it is bound to the instance for it to use when created in the room. This may seem confusing, so we'll just continue and add a fixture to an object and explain along the way.
Open the object
obj_Base and select the Uses Physics option then click the Physics button. This will open the object physics properties window with a number of extra options, shown below:
The first thing to set is the "collision shape", so click the Modify Collision Shape button to open up the Fixture Editor. This editor works the same as the Path Editor and permits you to move the points of the edge of the fixture to fit the sprite being used:
NOTE: We are making the fixture conform to the shape of the assigned sprite in this project, but this is NOT required. A fixture is independent of the sprite used, meaning you can define any shape fixture for an object, whether it has a sprite assigned or not, and regardless of the form of the assigned sprite.
When you have defined the shape for the floor object, you can close the Fixture Editor window to save it. It's time now to set the fixture properties. As this object is static, we need to tell the physics simulation to treat it as such, and that is done by setting the Density to 0. In real life an object with 0 density isn't possible, so this is the GameMaker way of flagging a fixture as being static. We'll quickly list the rest of the properties here now, although for this object you can leave them as their default values (except density):
Density: The density of something is defined as its mass per unit volume, which basically means how much mass is crammed into the space it takes up in the world.
Restitution: Restitution is really a way of saying how "bouncy" the fixture is. This setting will affect how much an object "bounces" when it collides with other objects and is co-dependant on other forces that act on the instance like gravity and friction.
Collision Group: The collision group can be used to force GameMaker Studio 2 to treat collisions in specific ways.
Linear Damping: Damping is used to reduce the physics world velocity of an instance as it moves over time (like air resistance).
Angular Damping: As above, only instead affecting the velocity of movement, this sets the slow down rate for the angular velocity.
Friction: Friction sets the loss of momentum caused by the collision of two instances with fixtures bound to them. The lower the value, the easier it is for one colliding instance to "slide" off the other.
You can find a more complete explanation of each of these properties from the manual, so we won't go into too much depth on them here, except where required for the test project.
Close the floor object now, and open the object
obj_Block. This is our first dynamic object and you should again select Uses Physics then click the "Physics" button to open the properties editor, then adjust the box fixture shape to suit the sprite in the Fixture Editor, as we did for the floor object. The default values are fine here too - the density set to 0.5 - so close the object once you've finished and then open the object
Once more, enable physics selecting the Uses Physics option then click the "Physics" button and open the Fixture Editor. This time we need to set the collision shape to "Circle", which can be done from the menu at the top left:
When you edit the collision shape now, you will see that it is no longer a fixture with four corner points, but rather a fixture with two points, one marking the center of the fixture and the other marking the radius. Set these to suit your sprite and close the editor when ready. Now, in the physics properties, set the density to 0.1, and set the restitution to 0.8. We do this because we want it to be lighter than the block fixture and bounce on contact with other fixtures.
For our final object, the triangle, we need to create a polygon fixture. This is a fixture that we explicitly define the points for, and in a very specific way. A polygon fixture has a minimum of 3 points and a maximum of 8 points and it cannot be concave. This is important to note as to make concave shapes you need to bind multiple fixtures which can only be done through code (and we'll cover it in a future tech blog). So, enable physics for the object then click the "Physics" button and open the Fixture Editor to edit the collision shape. In the editor, select the Convex Shape option for the collision shape. Now, not only can you move points around, but you can add and remove them to create your fixture:
Add and adjust three points now to suit the triangle sprite that we have assigned to this object, then close the editor. With that done, set the fixture properties to have a high density (0.8) and a high linear and rotational damping (0.6), but a low restitution (0.1) so it behaves slightly differently from the previous objects.
We are almost ready to test these objects and see how they react to each other, but before we can we need to make a controller object, so make a new object and call it
obj_Control. Give it an Alarm event with a comment, like "Spawn Timer" or something. This is simply to limit the number of instances of our physics objects spawned. Now add a Step Event with the following:
if (mouse_check_button(mb_left) && (alarm < 0))
instance_create_layer(mouse_x, mouse_y, layer, choose(obj_Block, obj_Circle, obj_Triangle));
alarm = 5;
Place this object in the room we created at the start, along with a number of the static
obj_Base objects all on the "Instances" layer. Now run the project! If all has gone correctly you should see something like this:
Congratulations! You've just created your first physics simulation in GameMaker Studio 2!
To round off this article, let's just run through some of the important points:
When physics is enabled, all collisions and movement are governed by the special physics variables and functions, and not the normal ones.
Physics bodies can be defined by one of three types: static, dynamic or kinematic.
For physics bodies to collide, the instance that has physics enabled must have a collision event, even if that event only has a comment.
An object is physics enabled when it has a fixture bound to it.
A fixture is a combination of a collision shape and basic physical properties.
Hopefully with this article you can see that basic physics is quite easy to set up and start working with. However you'll notice that apart from creating the objects themselves, there is no other interaction in the example. For that we need to start using the dedicated physics functions through the code editor, and we'll be covering that along with a few other more complex themes in a future tech blog.
You can download a .yyz for this tech blog showing the finished example here.