OverviewIn this tutorial, we will create a game where a Quorum bunny crashes into a tower of boxes with its deadly hops. Along the way, we will learn about new physics properties and concepts. The first concepts necessary for the bunny to knock over the boxes are mass and density.
Mass and Density
Up until now, all physics objects’ masses have been determined using a default mass of 1 kilogram. We can change this by setting either mass or density. Mass is how much stuff something is made of. It is related to weight. As mass increases, so does weight. Weight also relies on gravity (think of astronauts on the moon and how they weigh less there even though they have not changed themselves.) We can think of mass as how much our objects weigh as long as we remember that changing gravity will also affect their weight. Mass is set per item using this action:
Here mass is a number representing how many kilograms an item is. To help us understand the units of mass better, consider a few examples. The mass of a typical person is about 70 kg. The mass of a car might be between 1000 to 2000 kg, and the mass of a commercial airliner might be between about 40,000 kg and 75,000 kg. When mass is set this way, density is automatically calculated. Density is another way to think about mass. It is how much mass an item has per units squared, where a unit is determined by the camera. Normally a unit would be a pixel, but this might be different if we use the orthographic camera, for example. In that case, a unit is one, whatever that means to the camera. By units squared, we mean a one unit by one unit square. We can set density for an item using:
number mass = 5 itemName:SetMass(mass)
number density = 5 itemName:SetDensity(density)
density is a number representing the number of kilograms per units squared. If density is set like this, mass will be automatically calculated for the item.
FrictionIn the previous tutorial on force and velocity, the character was able to move across the platforms without slowing down. The physics property that provides resistance to movement across something is called friction. Sliding on ice or a puck moving in air hockey are examples of movement with very little friction. Pushing furniture on heavy carpet is an example of movement with high friction. To set an item’s friction we use:
number friction = 0.5 itemName:SetFriction(friction)
Here friction is a number between zero and one. Zero means no friction and one is the highest amount of friction. If we set friction to be a number higher than one, the system will treat it the same as if we set it to one.
RestitutionWe want our bunny to be relatively bouncy. To achieve this, we need a new physics property called restitution. Restitution can be thought of as the bounciness of an item. Like friction, restitution is set per item using:
Here restitution is a number representing how much bounce is preserved on impact. To understand this fully, we need a new concept: energy. When an item falls, the movement is a result of gaining kinetic energy. Kinetic means movement, basically. When the item is at the height of its fall before starting to move downward, it has no kinetic energy (it is not moving). Instead it has potential energy. During the fall, the item trades its potential energy for kinetic energy. When the item starts to bounce back up, it is trading its kinetic energy for potential energy. The restitution value, then, is multiplied by the energy at impact to affect how much kinetic energy the item has when it starts to bounce up. For example, a restitution of one means that the item retains all of its kinetic energy when it bounces. A restitution of zero means that the item loses all its kinetic energy, and therefore does not bounce. Realistic restitution, then, would be somewhere between zero and one, but it is possible to have a restitution greater than one.
number restitution = 0.4 itemName:SetRestitution(restitution)
CodeHere is a zip file containing a Quorum project using the code below. Read through, then use the following code to discover mass, friction, and restitution.
use Libraries.Game.Game use Libraries.Game.Graphics.Drawable use Libraries.Compute.Vector2 use Libraries.Game.Graphics.Color use Libraries.Interface.Events.KeyboardListener use Libraries.Interface.Events.KeyboardEvent use Libraries.Game.Graphics.OrthographicCamera use Libraries.Interface.Events.CollisionListener2D use Libraries.Interface.Events.CollisionEvent2D use Libraries.Sound.Audio use Libraries.Compute.Math class Main is Game, KeyboardListener, CollisionListener2D Math math Drawable ground Drawable bunny Vector2 gravity Vector2 character Color color Audio jumpSound Audio impactSound Audio movingSound action Main StartGame() end action CreateGame AddCollisionListener(me) AddKeyboardListener(me) EnablePhysics2D(true) OrthographicCamera camera camera:SetToOrthographic(20, 20) SetCamera2D(camera) ground:SetName("ground") ground:LoadFilledRectangle(20, 1) ground:SetPosition(0, 0) ground:SetColor(color:Green()) Add(ground) ground:EnablePhysics(true) ground:SetUnmovable() ground:SetFriction(1) bunny:SetName("bunny") bunny:Load("media/HourOfCode.png") bunny:SetPosition(1, 1) bunny:Scale(1.0/80) Add(bunny) bunny:EnablePhysics(true) bunny:SetResponsive() bunny:SetFriction(0.2) bunny:SetRestitution(0.75) bunny:SetMass(10) gravity:Set(0, -9.8) SetGravity2D(gravity) // We make invisible walls so that everything stays on screen. CreateInvisibleWall(-2, 0) CreateInvisibleWall(21, 0) CreateInvisibleWall(0, 21) // We create a tower of boxes. GenerateBox(0.6,1,color:Red()) GenerateBox(-0.6,1,color:Red()) GenerateBox(0,2, color:Orange()) GenerateBox(0.6,3,color:Yellow()) GenerateBox(-0.6,3,color:Yellow()) GenerateBox(0,4, color:Green()) GenerateBox(0.6,5,color:Blue()) GenerateBox(-0.6,5,color:Blue()) GenerateBox(0,6, color:Purple()) jumpSound:Load("media/Fwip.ogg") impactSound:Load("media/Boing.ogg") movingSound:Load("media/Bing.ogg") jumpSound:SetVolume(0.5) impactSound:SetVolume(0.3) end action Update(number seconds) // If the bunny is noticably moving, we play a sound to represent the bunny's position. if math:AbsoluteValue(bunny:GetLinearVelocity():GetX()) > 1 or math:AbsoluteValue(bunny:GetLinearVelocity():GetY()) > 1 movingSound:SetBalance(bunny:GetX()) movingSound:SetPitch(bunny:GetY()) movingSound:SetVolume(0.5) movingSound:Play() end end action PressedKey(KeyboardEvent event) if event:keyCode = event:SPACE Vector2 force force:Set(12800, 10000) bunny:ApplyForceToCenter(force) jumpSound:Play() end end action BeginCollision(CollisionEvent2D event) impactSound:Play() end /* To balance the tower of boxes, we make every other level have longer than tall boxes. The displaceFromGround parameter tells us how many levels of boxes up to place a box. For example, if it equals 2, then the box is placed two box heights up from the bottom of the screen. displaceFromCenter tells us how many box lengths from x = 11.5 we want the box to be placed horizontally. For example, if we want two boxes centered ontop of a box at center (x = 11.5) then we can use displaceFromCenter = 0.6 for one and -0.6 for the other. This will place them so that the gap between them is 0.8 box lengths centered at 11.5. */ action GenerateBox(number displaceFromCenter, integer displaceFromGround, Color color) Drawable box integer boxSize = 2 if displaceFromGround mod 2 = 0 box:LoadFilledRectangle(boxSize + 1, boxSize) box:SetPosition(11.5 + displaceFromCenter*(boxSize), (boxSize)*displaceFromGround) else box:LoadFilledRectangle(boxSize, boxSize) box:SetPosition(12 + displaceFromCenter*(boxSize), (boxSize)*displaceFromGround) end box:SetColor(color) Add(box) box:EnablePhysics(true) box:SetFriction(1) box:SetResponsive() box:SetMass(5) end action CreateInvisibleWall(integer x, integer y) Drawable invisibleWall /* We need two invisible walls for the sides that are taller than long, and one for the top that is longer than tall. The side walls' positions both have y = 0, so that is how we can tell which type is necessary. */ if y <= 0 invisibleWall:LoadRectangle(1, 22) else invisibleWall:LoadRectangle(21, 1) end invisibleWall:SetPosition(x , y) Add(invisibleWall) invisibleWall:EnablePhysics(true) invisibleWall:SetUnmovable() end end
In the next tutorial, we will discuss Joints In 2D, which describes how to use joints in a 2D game.