For the rest of the book we are going to focus on creating a single game from concept to a completed, released product. We will be utilizing everything we have learned so far and will be introduced to a variety of additional features, such as GameMaker: Studio's physics and particle systems. We will build some more systems to allow character dialog and an inventory. Finally, we will look at the different ways to release the game, including onto Facebook.
In this chapter we are going to build a physics-based tower toppling game that will demonstrate GameMaker: Studio's implementation of the Box2D open source physics engine. It will feature towers made out of a variety of different materials, such as glass, wood, and steel. The goal of the game will be to clear a restricted zone by destroying these towers utilizing a variety of tools. We will create TNT that will blast outwards, a Wrecking Ball that will swing down, and a Magnet that will attract loose parts. Best of all, all the collision and movement will be done by the engine itself!
When building a physics-based game, it requires a different way of thinking about how you go about creating things. So far, we have focused on applying movement to an instance by either teleporting it via the X/Y coordinates, or by changing the speed
, vspeed
, and hspeed
variables. When we use the physics engine, these properties are ignored. Instead, the system itself deals with movement by applying a force onto the instance. That instance will react to the force based on its own properties and will act accordingly.
Additionally, the direction of the world coordinates is not the same in physics world. Zero degrees in the GameMaker standard physics world indicates a direction of right, whereas in the Box2D physics world, zero degrees indicates up, as can be seen in the following diagram:
To fully understand how the Box2D physics engine works, we need to take a look at the following four components that it is comprised of:
Much like the real world, the physics world starts with the application of gravity. The amount of gravity will determine how fast the objects will fall and how much force is necessary to counteract it. Before we can use any of the physics functions in a game, we need to activate the world physics.
Chapter_06
.Sandbox
. We will use this Room for testing purposes only.0.0
and Y: 20.0
. This will set the direction and strength of the gravity in the world. If you wanted to have gravity as it is on Earth, we would set the value to Y: 9.8
. We are setting it to 20.0
, so objects appear to fall faster.The world is now ready to use the physics engine! The physics settings of the room should look like the following screenshot:
In order for something to be affected by gravity and other such forces, an object requires a Fixture. A Fixture is what defines the shape and properties of a physics object. We will need to build two Objects: a Ground object that will never move, and a Steel Pillar which will react to gravity.
spr_Ground
, and load Chapter 6/Sprites/Ground.png
with Remove Background unchecked. Leave Origin at X: 0
, Y: 0
, and click on OK.obj_Ground
, and assign spr_Ground
as the Sprite.The first element we need to set up is Collision Shape. There are three options to choose from: Circle, Box, and Shape. The most common shape is Box, which just has four points and is always in a rectangular form. The Circle shape is useful for perfectly round objects as it is determined by a radius, thus not useful for round shapes like an egg. Shape is the most useful option in that you can have up to eight points of collision. One drawback for this is that all shapes must be convex, or it will not work. See the following screenshot to better understand what is acceptable:
Now that the shape is complete, we can set the other physics properties. There are six adjustable properties available to us here:
Different materials in the real world will have different values for each of these properties. There are many charts available that will show values for many types of materials, such as steel that has a density of 7,820 kilograms per cubic meter, and has a friction coefficient of 0.78 when touching other steel. Trying to think of all these values as they correspond to objects in a game can quickly become overwhelming. Luckily, games don't need to use real-world values, but instead we can use general concepts for the materials, such as steel has a high density while ice has a low density. Below is a chart with some basic concepts for how we need to treat the values for Density, Restitution, and Friction. For Linear Damping and Angular Damping it is a bit trickier, as they relate more to the shape of an object. For example, a round steel pin would have less Angular Damping than a square steel pin. All of these materials, whatever we set the values to, should always be tweaked until they feel correct for the game they are in. It is completely valid for a metal bar to have a density of three in one game and 300 in another, so long as it acts as the developer intends.
Material |
Density |
Restitution |
Friction |
---|---|---|---|
Steel |
High |
Low |
Medium |
Glass |
Low |
Medium |
Low |
Wood |
Medium |
Medium |
Medium |
Rubber |
Medium |
High |
Medium |
Stone |
High |
Low |
High |
0
. When an object has no density it is considered to be a static object.0
.0
.0
.1
. We are done with obj_Ground
, so click on OK.spr_Pillar_Steel
, and load Chapter 6/Sprites/Pillar_Steel.png
with Remove Background checked. Center the origin and click on OK.obj_Pillar_Steel
, and set spr_Pillar_Steel
as its Sprite.20
.2
.0
, as we do not want to slow this object or have it bounce. We have now finished setting the properties of this object.obj_Ground
event. As can be seen in the next screenshot, we don't need any code, we just need a comment. Drag a Comment from the Controls tab under Actions: and write Collide with Ground
. With this little trick the Pillar will now have active collision with the Ground.Sandbox
room and place an instance of obj_Pillar_Steel
somewhere near the top in the center horizontally. Also, place instances of obj_Ground
along the bottom with one additional instance located right above the floor and just slightly under where the Steel Pillar will fall, as seen in the following screenshot. To move an instance freely in the Room Properties editor, hold down the Alt key while holding down the left mouse button.We have just completed our first physics simulation! Let's now take a look at Joints.
There are times when we will want two or more objects to be constrained to each other, such as a chain, or a ragdoll body. In the physics engine it is achieved through the use of Joints. There are five different types of Joints that we can use:
Let's take a look at how Joints work by creating a simple chain that is attached to an Anchor.
spr_Anchor
, and load Chapter 6/Sprites/Anchor.png
with Remove Background checked. Center the origin and click on OK.obj_Anchor
, and set spr_Anchor
as the Sprite.0
. We can leave the other properties at the default values and it should look like the following screenshot:spr_ChainLink
, and load Chapter 6/Sprites/ChainLink.png
with Remove Background checked. Center the origin and click on OK.obj_ChainLink
, and set spr_ChainLink
as the Sprite.50
.0
. The final settings should look like the following screenshot:scr_Anchor_Create
, write the following code, and add this to a Create event in obj_Anchor
:for (i = 1; i < 10; i++) { chain[i] = instance_create(x+ (i * 16), y, obj_ChainLink); }
To build the Chain we run a loop starting to create nine links of the Chain. We start the loop at 1
so that the Chain is offset correctly. We use a basic one-dimensional array to store the ID of each Chain Link, as we will need this when we add the joints. The x
offset we have in the creation will create each link an equal distance apart horizontally.
physics_joint_revolute_create(self, chain[1], self.x, self.y, 0, 0, false, 0, 0, false, false);
We start by creating a Revolute Joint from the anchor to the very first Chain Link. The rotation will occur around the Anchor's X and Y axes. The next three parameters relate to the limitations of the rotation: the minimum and maximum angles of rotation, and whether these limits are active. In this case we don't care, so we have turned off any angle limitation. The following three parameters are for whether the joint will rotate on its own or not, with values for the maximum speed, the set speed, and whether it is active. Again, we have turned it off so the Chain will just hang in the air. The last parameter is for whether the Anchor can collide with the Chain, and we do not want that to occur here.
for (i = 1; i < 9; i++) { physics_joint_revolute_create(chain[i], chain[i+1], chain[i].x, chain[i].y, -20, 20, true, 0, 0, false, false); }
Here we are using a loop again, so that we can go through each link and attach the one that follows. Notice that the loop stops at 9
, as we have already connected one piece of Chain. In the case of the Chain, we don't want each Link to have full freedom of rotation. We have activated the rotational limit and set it to 20 degrees in either direction.
Sandbox
and add a single instance of obj_Anchor
near the top of the room.In order to move an object in the physics world, excluding movement due to gravity, it requires that a Force be applied to it. These forces can be applied from a point in the world or locally to the instance. How the object reacts to the force depends on the properties it has. Just like the real world, the heavier the object, the more force is required to move it.
In order to take a look at Forces we are going to create TNT, which will explode, shooting out eight fragments. These fragments will be very dense and will require a lot of force to make them move.
spr_TNT_Fragment
, and load Chapter 6/Sprites/TNT_Fragment.png
with Remove Background unchecked. Center the origin and click on OK.obj_TNT_Fragment
, and assign spr_TNT_Fragment
as the Sprite.10
. We are making this value very high, so that when it collides with objects, such as the Steel Pillar, it will be able to move it.0
.scr_TNT_Fragment_Create
, with the following variables:mySpeedX = 0; mySpeedY = 0;
The strength and direction of a force is determined by a vector, which is why we need X and Y variables. We have set it to zero, so that it is not moving by default. Don't forget to apply this to a Create event in obj_TNT_Fragment
.
scr_TNT_Fragment_Step
, and apply some force. Add this script to a Step event.physics_apply_force(x, y, mySpeedX, mySpeedY);
The function physics_apply_force
is a world based force, with the first two parameters representing where in the world the force is coming from, and the second two parameters being the vector of force to be applied.
if (point_distance(x, y, xstart, ystart) > 128) { instance_destroy(); }
All we are doing here is checking to see if the Fragment has moved more than 128 pixels from where it was created. If it has, we remove it from the world.
scr_TNT_Fragment_Collision
and remove the instances.instance_destroy();
obj_Ground
event and add this script. This will remove the Fragment if it hits the Ground.obj_Pillar_Parent
. This is all that it needs for now, so click on OK.obj_Steel_Pillar
and set Parent to obj_Pillar_Parent
.obj_Steel_Pillar
, we might as well have it react to other Pillars as well. Add an obj_Pillar_Parent
and drag a Comment from Controls into the Actions: area, and enter Collides with Pillars
as the comment.obj_Pillar_Parent
and apply scr_TNT_Fragment_Collision
. We will now have collision with all Pillars!spr_TNT
, and load Chapter 6/Sprites/TNT.png
with Remove Background checked. Center the origin and click on OK.obj_TNT
, and apply spr_TNT
as the Sprite. We will be manually placing the TNT in the game, and we don't need it to react to the world physics, so we do not need to turn on Use Physics.scr_TNT_Activate
, and for testing purposes, add it to a Space event under Key Press. We are going to create only a single Fragment and have it launch outwards to the right, so we can see how forces work in the world.frag_01 = instance_create(x, y, obj_TNT_Fragment); frag_01.mySpeedX = 100;
We first create a Fragment and capture its ID in a variable. We are then setting the horizontal force to be 100 units. This value seems like it should be enough force to push this object to the right.
Sandbox
and place a single instance slightly to the left of where the Steel Pillar will fall and three grid spaces above the Ground. Also, let's remove the extra instance of Ground and the Chain. The Room should look like the following screenshot:scr_TNT_Activate
and change the second line to:frag_01.mySpeedX = 1000;
frag_01.mySpeedX = 10000;
frag_01 = instance_create(x, y, obj_TNT_Fragment); frag_01.mySpeedX = 10000; frag_02 = instance_create(x, y, obj_TNT_Fragment); frag_02.mySpeedX = -10000; frag_03 = instance_create(x, y, obj_TNT_Fragment); frag_03.mySpeedY = 10000; frag_04 = instance_create(x, y, obj_TNT_Fragment); frag_04.mySpeedY = -10000; frag_05 = instance_create(x, y, obj_TNT_Fragment); frag_05.mySpeedX = 5000; frag_05.mySpeedY = 5000; frag_06 = instance_create(x, y, obj_TNT_Fragment); frag_06.mySpeedX = 5000; frag_06.mySpeedY = -5000; frag_07 = instance_create(x, y, obj_TNT_Fragment); frag_07.mySpeedX = -5000; frag_07.mySpeedY = -5000; frag_08 = instance_create(x, y, obj_TNT_Fragment); frag_08.mySpeedX = -5000; frag_08.mySpeedY = 5000; instance_destroy();
As we can see, for each Fragment we apply an appropriate value for the forces in the X and Y directions. There is no need for us to pull out a calculator and some fancy equations to figure out exactly how much force is needed, especially on the angled pieces. Remember, this is a video game and we should only worry about the overall effect and experience the player has to see if the results are correct. When you run the game it should look something like the following screenshot:
At this point we have a good foundational knowledge of how the Box2D physics engine works. We have built a room with physics activated and created several objects with fixtures and physics properties. We have used joints to connect a series of instances together and we have applied forces to an object to make it move. We are now ready to start building the tower toppling game!
18.226.164.75