Physics Force and Velocity In 3D

Understanding force and velocity in a 3D physics enabled Quorum game

Overview

In this tutorial, we will discover new physics concepts by making a platformer game. Our goal is to propel a character represented by a ball forward through a series of platforms. We will do this using the concept of force which we can control in various ways. We will also need linear velocity. Linear velocity propels our character forward and forces can be used for jumping. To make platforms, we will use a concept that we call NonResponsiveness, which was mentioned in the Gravity in 3D tutorial. To use these ideas, we need a special concept from math: vectors.

Vectors

In math, a vector is like a line starting at a point and continuing out in one direction. It has a magnitude (length) and a direction. When we are using these for forces, we can look at the direction as being the direction we want the force to push towards and the magnitude as how strong the force is. When we are using these for linear velocity, we can look at the direction as being the direction the object will move in and the magnitude as how fast it will move. In Quorum for 3-dimensional games, we handle all of the details of vectors for you using a special class called Vector3. First we need to add a use statement to the top of the program:

use Libraries.Compute.Vector3

We can then set a Vector3 object like this:

number X = 0
number Y = -10
number Z = 0
myVector:Set(X , Y, Z)

The X value represents the direction and amount of force applied horizontally per second. The Y is the same for vertical force and Z is the same for force applied towards or away from the screen. It might be helpful for us to review the Drawing In 3D tutorial to remember how the three axes translate to the screen. This might look familiar to how we set gravity. In fact, gravity is represented in the system using a Vector3. Gravity pushes objects down, so we set the Y value to be a negative number. It does not push objects along the x or z axes, so the X and Z were set to 0. Gravity can be set using a Vector3 object as well:

Vector3 gravity
gravity:Set(0 , -10, 0)
SetGravity3D(gravity)

Linear Velocity

To give the character a continuous push forward, we need to set their linear velocity, and to do this, we need to create and set a Vector3 object. In the example code here, we want the character to move horizontally across the screen from left to right, so the X is set to a positive number. We don’t want them to have vertical movement, unless it’s from the force of a jump or gravity, and we do not want movement towards or away from the screen at all. The Y and Z are set to 0. Once we have the Vector3 setup, we can set the linear velocity of the character:

linearVelocity:Set(5, 0, 0)
character:SetLinearVelocity(linearVelocity)

Now the linear velocity will be applied to the character. We can think of this as how much the character will move each second. Note that the linear velocity is affected by forces, friction, gravity, and other physics properties.

Angular Velocity

If we want an item to rotate, we can do this with angular velocity. We can set it using:

Vector3 angularVelocity
angularVelocity:Set(0, 0, 100)
character:SetAngularVelocity(angularVelocity)

Note that angular velocity is set differently in 3D than in 2D. In 2D angular velocity was simply a number, and rotations could be clockwise or counterclockwise depending on the sign of the number. In 3D we can choose which axis we want an item to rotate about as well. Angular velocity is represented using a Vector3, where the x value represents the amount of rotation per second about the x axis. Likewise the y and z values of the Vector3 correspond to rotation about the y and z axes, respectively. Like linear velocity, it is applied to the character continuously, but can be affected by torque, friction, and other physics properties. A negative number for any of the three numbers in the Vector3 means clockwise rotation about that axis while a positive number means counterclockwise rotation.

Force

We used force previously when we added gravity, but forces can be used for much more than gravity. Forces can be made to push responsive objects in any direction with varying degrees of strength by setting the Vector3 appropriately. After we have a Vector3 object created and set, we can apply force in two ways. The first way to apply a force to an object is using the ApplyForceToCenter action. This action applies all of the force to the center of the object.

Vector3 force
force:Set(50, 300, 0)
itemName:ApplyForceToCenter(force)

The second way is to use the ApplyForce action which takes two Vector3 objects instead of one. The first is the force vector, and the second is the point the force will be applied to (represented by a Vector3 object.) The point should be in the form of a displacement from the center of the object the force is being applied to. For example, if we want the force be applied at a spot one to the right and one up from the center of the object, the point’s x and y variables would both be set to one and the point’s z variable would be zero. If we wanted to apply force to the center with this action, we would set the point’s x, y, and z variables to zero. Note that this is different in 2D where the point does not represent how far from the center, but where on the screen the force should be applied from.

Vector3 point
point:Set(0, 0, 0)
itemName:ApplyForce(force, point)

This action is useful when the force needs to hit the object at a particular spot. Additionally, if a force is not applied to the center of the object, a torque will be introduced, causing the object to rotate.

Torque

Torque is the angular component of a force, which means applying it will make the item spin. We can apply torque manually using:

Vector3 torque
torque:Set(0, 0, 100)
itemName:ApplyTorque(torque)

Torque is set in a similar way to angular velocity. The x, y, and z components of the Vector3 correspond to rotation about the x, y, and z axes. The difference between torque and angular velocity is the same as the difference between force and linear velocity. Force is a one time push, while linear velocity is the current speed. Applying torque is like giving the item a one time push to start it spinning, while angular velocity is the current amount of rotations per second applied continuously. A negative number for any of the three numbers in the Vector3 means clockwise rotation about that axis while a positive number means counterclockwise rotation.

Movement Outside of Physics

If we want to move an item without using physics, and without affecting the physics system, we can use SetPosition(), SetX(), SetY(), SetZ() or Rotate(). These actions are described in more detail in the Animation in 3D tutorial tutorial. Using these will leave velocities and forces intact upon arrival at the designated spot, but will also bypass the collision detection system and forces like gravity on the way.

Code

Here is a zip file containing a Quorum project that uses the code below. Read through, then use the following code to discover forces and linear velocity.

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.Game.Graphics.DirectionalLight
use Libraries.Game.Graphics.AmbientLight

class Main is Game, KeyboardListener, CollisionListener3D

   boolean platformMovesUp = false
   number totalSeconds = 0.0

   Model platformRight
   Model platformLeft
   Model character
   Vector3 gravity
   Vector3 linearVelocity
   Vector3 jump
   Color color

   Audio jumpSound
   Audio impactSound
   Audio platformSound

   action Main
       StartGame()
   end

   action CreateGame
       DirectionalLight light
       light:SetColor(0.8, 0.8, 0.8, 1)
       light:SetDirection(GetCamera3D():GetDirection())
       Add(light)

       AmbientLight ambient
       ambient:SetColor(0.4, 0.4, 0.4, 1)
       SetAmbientLight(ambient)

       AddCollisionListener(me)
       AddKeyboardListener(me)
       EnablePhysics3D(true)

       platformLeft:SetName("platformLeft")
       platformLeft:LoadBox(7, 1, 3, color:Green())
       platformLeft:SetPosition(-5, -2, 0)
       Add(platformLeft)
       platformLeft:EnablePhysics(true)
       platformLeft:SetNonResponsive()

       platformRight:SetName("platformRight")
       platformRight:LoadBox(7, 1, 3, color:Green())
       platformRight:SetPosition(5, -4, 0)
       Add(platformRight)
       platformRight:EnablePhysics(true)
       platformRight:SetNonResponsive()

       character:SetName("character")
       character:LoadSphere(1, 1, 1, "media/HourOfCodeDark.jpg")
       character:SetPosition(-5, -1, 0)
       Add(character)
       character:EnablePhysics(true)
       character:SetResponsive()

       linearVelocity:Set(1, 0, 0)
       character:SetLinearVelocity(linearVelocity)

       jump:Set(70, 350, 0)
       gravity:Set(0, -9, 0)
       SetGravity3D(gravity)

       jumpSound:Load("media/Fwip.ogg")
       impactSound:Load("media/Boing.ogg")
       platformSound:Load("media/Bing.ogg")

       jumpSound:SetVolume(0.5)
       impactSound:SetVolume(0.4)
   end

   action Update(number seconds)
       totalSeconds = totalSeconds + seconds
   // These if statements make a threshold of how far up and down the platform can travel
       if platformRight:GetY() > 1
           platformMovesUp = false
       end
       if platformRight:GetY() <= -4
           platformMovesUp = true
       end
   // The platform moves either up or down
       if totalSeconds  >= 0.01
           if platformMovesUp
               platformRight:MoveY(0.05)
           else
               platformRight:MoveY(-0.05)
           end
           totalSeconds = 0
       end
   /*
       To make the game endless, we move our character back to the left side 
       of the screen after he goes off the right side. 
   */
       if (character:GetX() >= 5)
           character:SetPosition(-5, platformRight:GetY() + 1, character:GetZ())
           character:SetLinearVelocity(linearVelocity)
   /*
       We set the height of the left platform to be the right platform's height so that it 
       seems like our character is continuing on the same platform as the one they were on
       when they went off the right side. 
   */
           platformLeft:SetY(platformRight:GetY())
    //  This math causes the height of the right platform to cycle through some different options.
           platformRight:SetY(((totalSeconds * 100) mod 6) - 4)
           if totalSeconds > 10
               totalSeconds = 0
           end
    // The platform switches directions when we go to a new screen. 
           platformMovesUp = true not= platformMovesUp
       end
    // The platform sound changes depending on how close the two platforms are to each other. 
       platformSound:SetY(platformRight:GetY() - platformLeft:GetY())
       platformSound:Play()
   end

   action PressedKey(KeyboardEvent event)
       Vector3 axis
        // When space is pressed, the circle jumps!
       if event:keyCode = event:SPACE
           character:ApplyForceToCenter(jump)
           jumpSound:Play()
       end
       // The ball will spin counterclockwise when the left arrow key is pressed.
       if event:keyCode = event:LEFT
           axis:Set(0, 0, 100)
           character:ApplyTorque(axis)
       end
   /*
       The ball will spin clockwise when the right arrow key is pressed. If the ball was already 
       spinning counterclockwise, then pressing the right arrow key will cause it to slow its spinning.
   */
       if event:keyCode = event:RIGHT
            axis:Set(0, 0, -100)
           character:ApplyTorque(axis)
       end
   end

   action BeginCollision(CollisionEvent3D event)
       impactSound:Play()
   end
end

Try it Yourself!

Press the blue run button to execute the code in the code editor. Press the red stop button to end the program. Your program will work when the console outputs "Build Successful!"

If we run this game, we should have a ball on a platform on the left side of the screen with a moving platform on the right. The ball will be moving towards the right side of the screen at a constant pace. The space bar can be used to jump to make sure the ball makes it to the right platform.

Next Tutorial

In the next tutorial, we will discuss Mass, Friction, and More In 3D, which describes how to use mass, friction and more in a 3D game.