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 cubed, where a unit is a length of one. By units cubed, we mean a one unit by one unit by one unit cube. 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 cubed. 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 that uses the code below. Read through, then use the following code to discover mass, friction, and restitution.
use Libraries.Game.Game use Libraries.Game.Graphics.Model use Libraries.Compute.Vector3 use Libraries.Game.Graphics.Color use Libraries.Interface.Events.KeyboardListener use Libraries.Interface.Events.KeyboardEvent use Libraries.Interface.Events.CollisionListener3D use Libraries.Interface.Events.CollisionEvent3D use Libraries.Sound.Audio use Libraries.Compute.Math class Main is Game, KeyboardListener, CollisionListener3D Math math Model ground Model bunny number depth = 8 number length = 20 number height = -3 Vector3 gravity Vector3 character Color color Audio jumpSound Audio impactSound Audio movingSound action Main StartGame() end action CreateGame AddCollisionListener(me) AddKeyboardListener(me) EnablePhysics3D(true) ground:SetName("ground") ground:LoadBox(length, 1, length, color:Green()) ground:SetPosition(0, height, depth) Add(ground) ground:EnablePhysics(true) ground:SetUnmovable() ground:SetFriction(1) bunny:SetName("bunny") bunny:LoadSphere(1, 1, 1, "media/HourOfCodeDark.jpg") bunny:SetPosition(-5, height + 0.5, 0) Add(bunny) bunny:EnablePhysics(true) bunny:SetResponsive() bunny:SetFriction(0.2) bunny:SetRestitution(0.75) bunny:SetMass(2) gravity:Set(0, -10, 0) SetGravity3D(gravity) // We make boundary walls so that everything stays on screen. CreateBoundaryWall(-length/2, height, depth) CreateBoundaryWall(length/2, height, depth) CreateBoundaryWall(0, height, length/2 + depth/2) CreateBoundaryWall(0, length + 1, depth) // We create a tower of boxes. GenerateBox(0.7,1,color:Red()) GenerateBox(-0.7,1,color:Red()) GenerateBox(0,2, color:Yellow()) GenerateBox(0.7,3,color:Blue()) GenerateBox(-0.7,3,color:Blue()) GenerateBox(0,4, 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()) > 0.5 or math:AbsoluteValue(bunny:GetLinearVelocity():GetY()) > 0.5 movingSound:SetBalance(bunny:GetX()) movingSound:SetPitch(bunny:GetY()) movingSound:Play() end end action PressedKey(KeyboardEvent event) if event:keyCode = event:SPACE Vector3 force force:Set(450, 860, 450) bunny:ApplyForceToCenter(force) jumpSound:Play() end end action BeginCollision(CollisionEvent3D event) impactSound:Play() end /* To balance the tower of boxes, we make every other level have wider 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 the center we want the box to be placed horizontally. For example, if we want two boxes centered ontop of a box then we can use displaceFromCenter = 0.7 for one and -0.7 for the other. This will place them so that the gap between them is 0.7 box lengths from the center. */ action GenerateBox(number displaceFromCenter, integer displaceFromGround, Color color) Model box number boxSize = 1.5 number center = 3 if displaceFromGround mod 2 = 0 box:LoadBox(boxSize * 3.5, boxSize, boxSize * 1.5, color) box:SetPosition(center + displaceFromCenter*(boxSize * 1.5), (boxSize )*displaceFromGround + height + boxSize/2, 7) else box:LoadBox(boxSize * 1.5, boxSize, boxSize * 1.5, color) box:SetPosition(center + displaceFromCenter*(boxSize * 1.5), (boxSize)*displaceFromGround + height + boxSize/2, 7) end Add(box) box:EnablePhysics(true) box:SetFriction(1) box:SetResponsive() box:SetMass(0.3) end action CreateBoundaryWall(number x, number y, number z) Model boundaryWall /* We need two invisible walls for the sides that are taller than long, one for the top that is longer than tall, and one in the back that is about equally long and tall. The side walls' positions both have y = 0, and the back wall is the only one with z > depth. That is how we can tell which type is called for. */ if z > depth boundaryWall:LoadBox(length + 2, length, 1, color:White()) elseif y <= 0 boundaryWall:LoadBox(1, length, length + 1, color:White()) else boundaryWall:LoadBox(length, 1, length + 1, color:White()) end boundaryWall:SetPosition(x , y, z) Add(boundaryWall) boundaryWall:EnablePhysics(true) boundaryWall:SetUnmovable() end end
In the next tutorial, we will discuss Introduction to Scene Editor, which describes development of Scene Editor in Quorum Studio.