Goals

The goals of this assignment are to learn to do the following:

Computer Science Principles Curriculum

Common Core Standards

Vocabulary

Overview

This project requires you to download this program template for the Frogger game, which will be completed in this assignment.

In this assignment, you will learn the basics of loading models on a scene, and how you can make them move using actions. We will take the example of a Frogger-style game (if you don't know Frogger, check out this Frogger information page. In this game, you can control (move) a character in order to reach a goal. On the way, you will encounter rows of moving obstacles. If you collide with one of the obstacles, you come back to the starting point and lose a life. When you lose all your lives, it is a Game Over. In our case, one mistake will be a Game Over.

Start this assignment by opening the given project template. We will look at the code that is given, and then complete the game by: making a given model move, loading and moving more obstacles, and setting the win conditions by managing collisions.

Optional Template Explanation

First, we will look at what some of the code in the given template does. Navigate to the CreateGame action in the template.

Before loading the character model, a Camera was added to look somewhere on the scene. It represents the starting point, and the character's position is chosen according to the direction of the Camera.

// the camera
PerspectiveCamera camera = undefined

action CreateGame

    /* initialize camera position, and direction */
    camera = cast(PerspectiveCamera,GetCamera3D())
    camera:SetPosition(-3, 1, 0)
    camera:LookAt(1, 1, 0) 
    camera:SetUp(0,1,0)
    /* *************************************** */

end

You can choose any value you want for the X, Y, Z values to adjust the camera angle. Feel free to experiment with these values to see how they affect things. In the case of this game we are looking 'down' the x-axis in the positive direciton. The character is added next:

// the character and the light
Model character
PointLight pointLight

action CreateGame
    // the following code comes after the camera setup 

    /* initialize character, his size and position */
    character:Load("Models/man.g3db") // load the g3db model
    character:Scale(0.005, 0.005, 0.005)
    character:SetPosition(1, -2, 0)
    Add(character)
    /* ************************* */

    /* Add light for the character */
    AmbientLight ambient
    ambient:SetColor(0.7, 0.3, 0.3, 1)
    SetAmbientLight(ambient)

    DirectionalLight light
    light:SetColor(0, 0.3, 0.4, 1)
    light:SetDirection(0.2, -0.8, 0.8)
    Add(light)

    pointLight:SetColor(1, 1, 1, 1)
    pointLight:SetPosition(0, 3, 0)
    pointLight:SetIntensity(20)
    character:Add(pointLight)
    /* ************************** */
end

The character comes from a g3db file. There are many file formats that a 3D model can take, but different file formats will not all work in the same game engine. In Quorum, for example, only 3D Models stored in a .g3db file or a .obj file will be able to be opened. Both of these file types are different ways of representing a bunch of points in 3-space. There are trade-offs in using one file or the other. .obj files cannot be animated, but they are not stored as binary, so it is easier to modify the model. .g3db files are a binary conversion of another file format that is better for larger models.

We load the file using Load(FilePath) from the Model class. Since the Model is too big for our scene when it loads, we scale (using the Scale action) it by reducing his width, height and depth to 0.005 of the original. You can also adjust these values if you wish.

Now that we have our character in front of the camera, we want the camera to follow the character, no matter what his move will be. Each Quorum program has an action called Update(number seconds), which is called multiple times per second. We manage all of the actions that deal with movement and changing values in the Update action.

Navigate to the Update action in the template.

// these are the values that we will use to move the character
number positionX = 0
number positionY = 0
number positionZ = 0

action Update(number seconds)

    /* we move the character every update depending on the values of the 3 fields */
    character:Move(positionX*seconds, positionY*seconds, positionZ*seconds)
    /* *** */

    /* we need the camera to move according to the character position */
    camera:SetPosition(character:GetPosition():GetX()-4,character:GetPosition():GetY()+3,character:GetPosition():GetZ())
end

We use the seconds parameter in order to move the camera smoothly. Set the camera position according to the position used in the default settings. (At the beginning, the camera X-position was -4 and the character position was 1, and the difference between these two values is -4. That is why we subtract 4 from the character X-position. The same calculation applies to the Y and Z position).

So we have looked at the code for a camera, a character, and to have the camera follow the character. Now we will look at the code that applies to movement. In order to make movement clear when the camera stays on a character, a scene needs stationary objects. The template adds 2 walls and a floor so that the movement will be more noticeable.

This code can be found at the beginning of the Main class:

// The model for the wall and their color
Model wallLeft
Model wallRight

// a floor
Model floor

Color grey
Color red

action CreateGame
    // the following code comes after the character and lighting setup

    // Create and add wall on the scene
    grey:SetColor(128, 128, 128, 1)
    red = red:Red()
    wallLeft:LoadBox(100, 20, 5, grey)
    wallLeft:SetPosition(30, -2, -32)
    wallRight:LoadBox(100, 20, 5, grey)
    wallRight:SetPosition(30, -2, 32)
    floor:LoadBox(90, 0.8, 68, grey)
    floor:SetPosition(40,-4,0)
    Add(wallLeft)
    Add(wallRight)
    Add(floor)
end

Goal 1 : Moving a character using 4 actions

Example

To move our character, we will write actions. In the template, the action MoveForward() has already been written. Right beneath the MoveForward() action, you will find headers for the actions MoveBackward(), StrafeRight(), and StrafeLeft(). Each of these actions, including MoveForward() has one parameter: a number called speed. Let's take a look at the MoveForward() more closely:

Navigate to the MoveForward() action toward the end of the program template.

action MoveForward() 
    positionX = speed       //By setting the X position, we move the character forward
    positionY = 0
    positionZ = 0
end

This action is responsible for updating 3 variables that are used to move the characters. Right now, our character can only move forward. We need to finish the remaining 3 movement actions.

Activity

Finish the 3 others actions: MoveBackward(number speed), StrafeLeft(number speed), and StrafeRight(number speed) based on the MoveForward(number speed) action. Think about which coordinates (x, y, or z) should be changed to move the character backward, to the left and to the right. Remember, we are looking down the x-axis. Imagine the character is moving in a positive direction on the x-axis as you have him move forward.

Goal 2 : Add boxes and move them

After the character, we need to add the obstacles. We will use the primitive boxes from the Model class to do it.

Example

This example comes from the program template. Right now, there is only one obstacle.

The first step to load one of these box obstacles is threefold. We have to

// The box we are using
Model box1

// We have to choose a color for the box
Color colorBox1

// We put a sound on a model
Audio3D soundCar

Activity

To make our Frogger game better we need to add more obstacles. Following the example above, let's instantiate 3 more boxes. Don't forget to instantiate an Audio3D object and a Color for each box we add.

Example

The next step in creating an obstacle is to load the model (the box in our case). The action LoadBox() from the model class requires three parameters: the width, height, and depth of our box and the color we want our box to be.

When we load a box, we must do the following:

When we set the color of our color objects, we call the color action

SetColor(number redValue, number blueValue, number greenValue, number alphaValue)
SetColor(number redValue, number blueValue, number greenValue, number alphaValue)

Each parameter should be a decimal value between 0 and 1. 0 means that the color (red, blue, or green) is totally absent. 1 means the complete presence of that color. The fourth number, the alpha value, sets the color transparency, but we should leave this as 1 for this project.

Navigate to the CreateGame() action to see the example where we load our first box.

// in CreateGame

/* Position, color of a box */
colorBox1:SetColor(0, 0, 255, 1)
box1:LoadBox(2,2,2,colorBox1) // width, height, depth = 2
Add(box1) // we add it to scene
box1:SetPosition(10, -2, 0)

Activity

Following the example of our first box, load, add, and set the position of our 9 new boxes. Set the positions so that two boxes exist on the same row (horizontal line across the screen) but not the same place. This should create 5 rows of obstacles.

Example

The next step is adding the sound to our boxes. We can add the Audio3D directly to our model so that the sound will have the same position as our obstacle. When an Audio3D object is added to a model, the following steps must be done:

In the CreateGame action, find the code below. This is our example which adds our first Audio3D object to our first model.

// in CreateGame 

// Enable the audio file and attach it to the box
soundCar:LoadToStream("Sound/carSound.wav")
soundCar:SetVolume(0.3)
soundCar:Play()
soundCar:EnableLooping()
box1:Add(soundCar)

Activity

Following the example above, add one Audio3D object to each of the boxes we have created.

Example

The last thing we need to do for our obstacles is make them move. This is done in the Update(number seconds) action. When a box hits one wall, it should be moved back to the starting position (z-value 10 or -10).

Navigate to the Update action and let's look at the code to move our obstacle across the screen.

// every update, we move the boxes along the z-axis
box1:Move(0, 0, seconds * -5)

// when a box hits the other wall, we have to put it back to the original position 
if box1:GetZ() < -10 // -10 is the Z-Axis position of the left wall 
    Remove(box1) // we remove it from the scene temporarily
    box1:SetZ(10) // the starting point
    Add(box1) // we put it again on the scene
end

In this example, we make the box move from 10 to -10 on the Z-axis (where our walls are located). Now we need to move the rest of our obstacles. You can try varying the speed of your different obstacles as well to make your game a bit more challenging.

Activity

Following the above example, in the Update action, call the Move action of each box and write a conditional to reset the position when it reaches the wall. Try making the two different rows of boxes go opposite directions.

The final aim of this section is to have 2 rows with 2 boxes on each (with different colors if possible) that are moving from one wall to another. For the first row, the boxes should go from left to right. For the second row, they should go from right to left.

Goal 3 : Win or lose the game

Now we have to set the win/lose conditions of our game. The program template has some of the code written for us.

Example

Our game has two possible endings: we hit a box and we lose the game or we reach the goal and we win the game. The template manages the win condition (collision with the goal), and manages collisions with our first box. The template uses the action

IsColliding(Model character, Model box)

which returns a boolean value indicating whether the two parameters are colliding.

We can use an action that returns a boolean value in our conditional statements. In this case, we want to call the action Lose() if the character is colliding with the box. Let's look at the win/lose conditional statement in our template. This is in the Update action of our program.

The general idea of this conditional statement is: if there is a collision with a box, you lose the game. Otherwise, if there is a collision with the goal, you win the game. If neither of those things has happened, the game should play normally. This is what that looks like in the template:

if IsColliding(character, box1) // if the player is hit by a box, GAME OVER
    Lose()
//elseif {check box 2 collision and then check for the remaining boxes}
elseif IsColliding(character, goal)// if he hit the goal, victory
    Win()
else
    //this is where the code to move the boxes is (from Goal #2)

    //this is where the audio management code is (completed in the next activity)

end

Activity

Following the above example, modify the conditional statement (by adding more elseif statements) to check the collision for the new boxes you have added. Each time you call the

IsColliding(Model character, Model box)

action, be sure to pass the character and the box you are checking. If any of them return true, call the Lose() action.

Example

The last thing that must be done in order to play the game, is managing the Audio for our boxes. When our character is near a box, we need to stream the car sound for that box. Our program template has an action

IsNearCar(Model character, Model box, Audio3D soundCar) returns boolean

which, like the previous IsColliding action, returns a boolean value that indicates if the character is near the box. If the character is near box1, we Stream the audio attached to box1 (soundCar1). This is what the code to do this for box1 look like:

// every update we check if the character is near a box
if IsNearCar(character, box1, soundCar1)
    // if the character is near a box we play the sound car
    soundCar1:Stream()
//elseif {check nearness for box 2 and then for the rest of the boxes}
end 

Activity

Following the above example, and using the IsNearCar action, modify the conditional statement to manage the audio for the boxes you have created. Remember to pass the character, the box you are checking, and the audio for that box.

Now, you have a basic Frogger style game. To make this game even better, you can add more rows of boxes.

Collaborate

Collaboration is an important part of programming. When you work with other programmers, you get other points-of-view that make your programs even better. The final part of this assignment requires you to learn how to use Git, a great tool for collaborating. Head over to the Teams and Version Control Tutorial if you need to know how to use Git.

Collaboration Activity:

When multiple programmers work on the same project, it is easier to find and fix any problems. The more people who collaborate on a project, the more points-of-view you get, and the easier it is to find errors and improve. This last activity will help grow your network of programmers.

Optional Collaboration Activity

Social media has changed the way that we communicate. It has provided a simple way to disseminate or broadcast information. It continues to develop new ways to communicate. Take advantage of this modern form of communication by creating a social media account (if you are older than 13) and sharing your Quorum Game. Invite your friends to download and play your game.

Next Tutorial

In the next tutorial, we will discuss Assignment 4.2, which describes an introduction to file saving and action overloading..