Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more

Tech

Physics In GameMaker: Studio - Part 3

Posted by Mark Alexander on 17 October 2014

In this last part of out three part mini-tutorial series about physics in GameMaker: Studio, we are going to explore joints, advanced physics world functions, and debug drawing. If you haven't already read through the previous parts of this series, you can find them from the following links:

Set Up the Physics World

in this article, we are starting from scratch so create a new project and call it "physics_joints" or something similar. Now add a single room, and set the background colour to dark grey or black (you'll see why later), then close the room editor. We aren't going to use the physics tab in the editor to set up the physics world this time, but instead we are going to do it in code.

Create a new object now and call it "obj_Control", then give it a Create Event with the following code:

physics_world_create(0.1);                      //Pixel to meters scale
physics_world_gravity(0, 0); //No gravity physics_world_update_iterations(20); //iterations per step physics_world_update_speed(60); //Update speed

The first two lines are the same as the option in the Room Editor for setting the pixels-to-meters scale and the gravity vector (which in this case we set to 0 to make things simpler), but the last two are slightly more complex and don't appear in the Room Editor.

The first deals with the update iterations. This value is the number of times that the physics will iterate through the collisions and movement etc... per step. So, here we are stating that the physics simulation should iterate through everything 20 times a step. This is a very powerful function as it means that you can make the simulation more or less precise by setting this to a higher or lower value.

the second is the update speed. This is the number of update steps per second that the physics simulation will run. Normally this value defaults to the room speed, but you can set it to a higher or lower value to increase or decrease precision. For example, a room speed of thirty and an update of 60 will force the physics simulation to run an update twice for every game step. Note that this is independant of the iteration speed, and will be cumulative with it, ie. an iteration speed of 20 and an update speed of 60 in a 30fps room will give 40 iterations per step.

In general you won't want to use these functions (and they are only here to show you how they are used, but they aren't strictly necessary for this demo), but if you have a game that requires very fast movement then you may want to up the iterations, or if you have a game that has a lot of physics bodies but that doesn't require such precision, you could lower the update speed and reduce the CPU load.

Our next task is to "box in" the room, so that we can bounce fixtures around without them leaving, so after the world code, add this:

var edge_fix = physics_fixture_create();
physics_fixture_set_chain_shape(edge_fix, true);
physics_fixture_add_point(edge_fix, 0, 0);
physics_fixture_add_point(edge_fix, room_width, 0);
physics_fixture_add_point(edge_fix, room_width, room_height);
physics_fixture_add_point(edge_fix, 0, room_height);
physics_fixture_set_density(edge_fix, 0);
physics_fixture_set_restitution(edge_fix, 0.5);
physics_fixture_bind(edge_fix, id);
physics_fixture_delete(edge_fix);

If you've followed these tech blogs so far, you should recognise this code as being similar to that which we used for defining a polygon fixture. The difference this time is that we are using it to create a static chain fixture, which is special fixture made from a number of connecting edge fixtures. Note that this only gives the room a border because the instance for the controller is placed at (0,0) in the room. If you place it elsewhere then it will not correctly bound the room.

Using Debug Mode

Another thing we are going to use in this example is the physics debug mode. You can call the function physics_world_draw_debug() in the draw event to force GameMaker: Studio to draw a number of different physics related values, like the fixture shape, the rotation angles, joints, etc... and in this demo we are going to use this function to draw the fixtures rather than give them sprites (Technically, you could code a whole game with no sprites and draw it using this function! However, it won't be pretty...).

This function takes a single value which is a bit mask. Different things can be rendered by setting the appropriate flag to mask or un-mask them, so we need to set that value in the Create Event of out controller object (you can find a complete explanation of flags and bit-masking from the tech blog: LiquidFun Physics Particles):

render_flags = phy_debug_render_shapes | phy_debug_render_joints | phy_debug_render_coms | phy_debug_render_obb;

You can find a complete list of the available flag constants from the manual. To draw the physics we need to now add a Draw Event with this code:

physics_world_draw_debug(render_flags);

Now when we add instances with fixtures to our room, they will be drawn using this function.

Creating Instances

We are almost ready to start adding instances with fixtures to our room, but before we do, we need to make a "base" object. We are going to create all instances and fixtures using code, but we need an object to create an instance of to bind the fixtures to, so make a new object now and call it "obj_Base". You will need to add a Collision Event with itself containing a single comment to ensure that collisions will happen.

We can now go back to our controller object and assign the base object as it's parent (this is so that the collision event is inherited and the instances will collide with the chain fixture we boxed the room in), then open the Create Event. We need to create two instances of our base object and bind a ficture to them before we can create the joint, so we do that by adding the following code:

var instA = instance_create(100, 100, obj_Base);
var instB = instance_create(100, 300, obj_Base);
var fixA = physics_fixture_create();
physics_fixture_set_circle_shape(fixA, 20);
physics_fixture_set_density(fixA, 0.5);
physics_fixture_set_restitution(fixA, 0.8);
physics_fixture_bind(fixA, instA); physics_fixture_bind(fixA, instB);
physics_fixture_delete(fixA);

We also need to add a Global Mouse Left pressed event to create an impulse and so move the instances, otherwise you won't be able to see how the joints work, so add that now with this code:

with (obj_Base)
{
var dist = point_distance(x, y, mouse_x, mouse_y);
if dist < 100
    {
    var pd = point_direction(x, y, mouse_x, mouse_y) - 180;
    physics_apply_impulse(mouse_x, mouse_y, lengthdir_x(100, pd), lengthdir_y(100, pd));
    }
}

You can now add an instance of "obj_Control" into the room and test your game. If all has gone well you can bounce a few "balls" around by clicking near them with the mouse.

Adding A Distance Joint

Let's connect those two circle fixtures with a joint. Joints are defined in world space not local space, so you should create the instances and place the joints based on their room coordinates. In this case (and still in the "obj_Control" Create Event) we would do it like this, after the code where we cretaed the instances and defined their fixtures:

physics_joint_distance_create(instA, instB, instA.x, instA.y, instB.x, instB.y, true); 

You supply the two instances to join, where in the room to join them, and then whether they can collide between themselves or not. All pretty straightforward! Run the room again now and see how you have a "barbell" like fixture that bounces around the room.

NOTE: You can only see the joints when debug drawing! In a normal game they are not visible, therefore you will need to draw some graphics yourself if you want to show a join on-screen.

Adding A Revolute Joint

Now you've seen how easy it is to join to fixtures, let's add a couple more but with a different type of joint... A revolute joint. This is a joint that is created at a position in the room and that joins to instances around that point. think of a drawing pin going through two circles of card. you can then rotate the two circles around the pin. Well, our revolute joint is going to do the same thing.

Still in the "obj_Control" Create Event, add the following:

var instA = instance_create(room_width - 300, 200, obj_Base);
var instB = instance_create(room_width - 300, 400, obj_Base);
var fixA = physics_fixture_create();
var fixB = physics_fixture_create();
physics_fixture_set_circle_shape(fixA, 100);
physics_fixture_set_density(fixA, 0);
physics_fixture_bind(fixA, instA);
physics_fixture_set_box_shape(fixB, 25, 100);
physics_fixture_set_density(fixB, 0.5);
physics_fixture_bind(fixB, instB);
physics_fixture_delete(fixA);
physics_fixture_delete(fixB);

physics_joint_revolute_create(instA, instB, room_width - 300, 300, 0, 0, false, 100, 50, true, false);

As before, we create two fixtures (a circle and a box) and we want to join the box to the outer edge of the circle. We do this with the revolute joint, and we also give the joint a motor (this is part of the function) so that it rotates. Note that we switch off collisions so that the instances ignore the collision event if they collide with each other.

Most joints have a few extra parameters to them, like this one which permits you to limit the angle of rotation and add a motor, so make sure to read the manual for each function you use to be clear on what each argument does.

Adding A Pulley Joint

The last joint we are going to add is a pulley joint. This will connect two instances two two positions in the world space, but permit them to move around within the constraints of the pulley, ie: if one moves to the right, then it will "pull" on the pulley joint and cause the other instance to move too. This one may seem a bit complicated, but as you'll see, it's similar to the previous joints:

var instA = instance_create(300, room_height - 100, obj_Base);
var instB = instance_create(room_width - 300, room_height - 100, obj_Base);
var fixA = physics_fixture_create();
physics_fixture_set_circle_shape(fixA, 20);
physics_fixture_set_density(fixA, 0.5);
physics_fixture_set_restitution(fixA, 0.8);
physics_fixture_bind(fixA, instA);
physics_fixture_bind(fixA, instB);
physics_fixture_delete(fixA);
physics_joint_pulley_create(instA, instB, 300, room_height - 180, room_width - 300, room_height - 180, 0, -2, 0, -2, 1, false);

Again, two instances, each with fixtures and positioned in the room. We then add the pulley joint by selecting the fixtures, then setting the first and second anchor points for the pulley. These anchor points can be anywhere in the room, and the distance between the instance and each anchor point is the amount of "give" that each end of the pulley will have. You can also set a "local" x and y offset for the joint, and this will make the connection between the pulley and the anchor offset from the base fixture. Finally you can add a ratio (in this case 1:1), which will make one end of the pulley have a larger or smaller length ratio compared to the other end of the pulley.

Summary

This final part of our physics tech blogs has hopefully introduced you to the final piece of the physics game-making puzzle. With the ability to create joints, and the techniques shown for debugging, you should now be able to make some simple physics game, and have a firm base for creating more complex games with a little help from the manual!

You can download an example file with the code in this tutorial here: Physics_Joints.gmz

Back to Top