Main

Tutorial 3

Tutorial 3: The OneButton-game

In this tutorial we will make a very simple game: OneButton. This is a game where a single button is used for control.

It is best if you have read the first two tutorials before starting this one.

Here is a picture illustrating the gameplay:

First we will create the main active components in our game: Player and Walls. In ZGameEditor a moving graphical entity and its behaviour is called a Model. So we need a PlayerModel and a WallModel.

Creating the Player model

Start with defining the player model.

  • Start ZGameEditor.
  • To start a new project: Select File - New project.

A model needs 3D-geometry for it to display itself. This is called a Mesh. The first component we create is the PlayerMesh.

  • Add (Ctrl-A) a Mesh-component to the Content list property.
  • Set the following property on Mesh:
    • Name = PlayerMesh

Now expand the PlayerMesh tree node to expose the Producers list property. This is a list of component that is used to generate (or produce) the mesh.

  • Add a MeshSphere-component to the Producers tree node.
  • Set the following properties:
    • Scale = 0.5 0.5 0.5
    • ZSamples = 3
    • RadialSamples 4

If you click on the PlayerMesh-node you will see that we now have created a small diamond shaped mesh.

In this game we want to use flat-shaded geometry. So the next component we need is a Material.

  • Add a Material-component to the Content list property
  • Set the following properties:
    • Name = SpriteMaterial
    • Shading = Flat

And create another material that we will use later on in this tutorial: the TextMaterial.

  • Add a Material-component to the Content list property.
  • Set the following properties:
    • Name = TextMaterial
    • Color = (click on the color and select a bright yellow color)
    • Blend = Alpha/OneMinusSourceAlpha

Now we are ready to create the model representing the player.

  • Add a Model-component to the Content list property.
  • Set the following properties:
    • Name = PlayerModel
    • CollisionBounds = 0.5 0.5

Notice that CollisionBounds have four edit fields, but we only set the first two. This is because the default collision style in ZGameEditor is 2D rectangular collision. The first value is width and the second is height.

Next we will control how the PlayerModel will be rendered on screen.

  • Expand PlayerModel to display the models component lists.
  • Add a UseMaterial-component to the OnRender list.
  • Set the following properties:
    • Material = SpriteMaterial

We will use the same material for both the player and walls. In order to display them in different color we use a RenderSetColor component.

  • Add a RenderSetColor component to OnRender.
  • Set the following property:
    • Color = Click the color and set Red: 217, Green: 252, Blue: 245 in the color selector dialog.

Now we can render the mesh.

  • Add a RenderMesh component to OnRender.
  • Set the following property:
    • Mesh = PlayerMesh

That's it for displaying the player-model. Now let's add some logic for how the player will move. This is done in the OnUpdate component list of the model.

  • Add a KeyPress component to OnUpdate.
  • Set the following properties:
    • Comment = Test mouse-click or up-key
    • Keys = ^{

The KeyPress component tests for user input. Comment property is used for typing comments that will help remembering how the application works without looking at all the property values. Keys property is set to which input to test, in this case we test for Arrow-Up (represented by special character "^") key and left mouse button click ("{").

  • Expand the KeyPress component to make the OnPressed list property visible.
  • Add a ZExpression component to OnPressed on KeyPress.
  • Click the Expression property on the ZExpression component. This will make the expression custom text editor appear.
  • Write the following code in the expression custom text editor:
    • CurrentModel.Velocity.Y+=10.5*App.DeltaTime;
  • Click the OK-button to save the expression.

So what did we just do? When the user clicks the mouse or press the arrow Up-key, the ZExpression will be executed. The expression adds a value to the Y (vertical) velocity. This makes the model accelerate upwards.

Next some movement logic and constraints to the player model:

  • Add a ZExpression component to OnUpdate on PlayerModel.
  • Click the Expression property on the ZExpression component. This will make the expression custom text editor appear.
  • Write the following code in the expression custom text editor:
CurrentModel.Velocity.Y-=5.5*App.DeltaTime;
CurrentModel.Velocity.Y=clamp(CurrentModel.Velocity.Y,-6,5);

if(CurrentModel.Position.Y<-4) {
  CurrentModel.Position.Y=-4;
  CurrentModel.Velocity.Y=0;
}

if(CurrentModel.Position.Y>4) {
  CurrentModel.Position.Y=4;
  CurrentModel.Velocity.Y=0;
}

CurrentModel.Rotation.X+=0.25 * App.DeltaTime;
  • Click the OK-button to save the expression.

This is what the expression does:

  1. Decrease the Y-velocity: This gives a downward acceleration which simulates gravitation.
  2. Test that velocity do not get too high.
  3. Test that the player do not move outside the screen.
  4. Rotates the model over the X-axis.

Delta-time explained

Why the multiply with App.DeltaTime? This is to make sure your game works in the same speed independent of the frame update interval.

Imagine a very slow computer that is only able to display 10 frames per second. OnUpdate is called between each frame. DeltaTime is set to the time-difference between last frame. So on the slow computer this will be 1/10 = 0.1 seconds. After one second of game time, velocity will be 0.1 * 10 frames = 1.0.

On the other hand, a fast computer may display 100 frames per second. Then DeltaTime becomes 1/100 = 0.01 seconds. So after one seconds we have velocity 0.01 * 100 = which is again 1.0. Thanks to multiplying with DeltaTime we have the same effect on both the slow and the fast computer after one second.

Without DeltaTime, velocity would be 10 times higher on the faster computer. That would make the game too slow and boring on the slow computer, and too fast and impossible to play on the fast computer!

So that is why DeltaTime is important to use!

Defining game variables

Variables are components that are used in expressions for holding values that are important for the application.

In our game, we will have two such values to keep track of:

  1. GameTime : this will hold the time in seconds that the current game have been played. We will use this for increasing difficulty in the game.
  2. Score : for keeping track of player score.

Here is how you create the variables:

  • Add a DefineVariable component to OnLoaded on the application.
  • Set the following property:
    • Name = GameTime
  • Add a DefineVariable component to OnLoaded on the application.
  • Set the following property:
    • Name = Score

Creating the Wall model

Next is the Wall model. Walls are the obstacles that the player have to dodge.

We create the wall in the same way we created the player. Start with the geometry.

  • Add (Ctrl-A) a Mesh-component to the Content list property on App.
  • Set the following property on Mesh:
    • Name = WallMesh
  • Expand the WallMesh tree node to expose the Producers list property.
  • Add a MeshBox-component to the Producers list property.
  • Set the following properties:
    • Scale = 0.25 1.2 3

Click the WallMesh component for preview. The wall is a vertical standing box.

Continue to create the Model for the wall.

  • Add a Model-component to the Content tree node.
  • Set the following properties:
    • Name = WallModel
    • Category = 1
    • CollisionBounds = 0.25 2.2

Category and CollisionBounds are used for collision detection.

Render the wall:

  • Expand WallModel to display the models component lists.
  • Add a UseMaterial-component to OnRender on WallModel.
  • Set the following property:
    • Material = SpriteMaterial

Use a orange color for the walls:

  • Add a RenderSetColor component to OnRender on WallModel.
  • Set the following property:
    • Color = Click the color and set Red: 255, Green: 130, Blue: 0 in the color selector dialog.

Render wall geometry:

  • Add a RenderMesh component to OnRender on WallModel.
  • Set the following property:
    • Mesh = WallMesh

Now for the Update-logic for the wall. The wall will move to the left. We need to check if the wall have reached outside the screen and if that is the case remove it.

  • Add a Condition component to OnUpdate.
  • Set the following properties:
    • Comment = Remove wall if outside screen
    • Expression = return CurrentModel.Position.X<-7.5;

The expression will return true when the models X (horizontal) position is lower than -7.5. The default camera in ZGameEditor have origo in the center of the screen with X rising from a negative value in the far left, to a positive value in the far right of the screen.

In this case -7.5 is a position outside the left edge of the screen, including some extra space to make sure no part of the wall is visible when it is removed.

  • Expand Condition to display OnTrue and OnFalse list properties.
  • Add a RemoveModel to OnTrue
  • (no properties are needed for RemoveModel, it just removes the current model from the screen)

We want to initiate every wall with some unique property values on creation. A Model instance is created when the SpawnModel-component is executed. The first thing a newly spawned model do is running the commands in the OnSpawn-list property.

  • Add a ZExpression component to OnSpawn on WallModel.
  • Click the Expression property on the ZExpression component.
  • Write the following code in the expression custom text editor:
CurrentModel.Position.X=8;
CurrentModel.Position.Y=random(0,4);
CurrentModel.Velocity.X=-3 - clamp(GameTime*0.1,0,2.5);
  • Click the OK-button to save the expression.

The expression does the following:

  • Set the X position to 8, this just outside the right side of the screen.
  • Sets the Y (vertical position) to a random value between -4 and 4.
  • Set the X velocity to a negative value. This makes the wall move left. The speed of the wall will increase with the GameTime-variable, this makes the game more difficult over time.

This is how the finished wall model should look like on your screen:

Ok, we have our created our player and wall objects. Let's move on to define the application logic.

Application states

An application state is a part of your application that has a distinct behaviour, such as waiting for a keypress or displaying a message. Only one state can be active at once. This is called the current application state.

Some examples of typical states for a game application:

  • Game playing
  • Game paused
  • Game over
  • Player loses a life
  • Restart current level

In our game we will have two different application states:

  • The title screen: display a message and wait for a keypress.
  • The PlayingState: the state when the game is being played.

The ZGameEditor component that defines a application state is called AppState.

Note: It is not required to use AppState-components. Simple applications may not need them at all, and different application behaviour can be achieved with using Condition-components in OnUpdate and OnRender. But using AppStates can make your project cleaner and require less effort when you want to make changes.

First we create the PlayingState.

  • Add a AppState component to States on App.
  • Set the following property:
    • Name = PlayingState

Now define what will happen when when the player wants to start a new game. This is done in PlayingState.OnStart.

  • Expand PlayingState to display the list properties.
  • Add a SpawnModel component to OnStart on PlayingState.
  • Set the following properties:
    • Model = PlayerModel
    • Position = -2 0 0

This will spawn the player model at the X=-2 position, a bit left of center. Next for some variable initialization.

  • Add a ZExpression component to OnStart on PlayingState.
  • Set the following properties:
    • Comment = Init game vars
  • Click on the Expression property.
  • Write the following code in the expression custom text editor:
GameTime=0;
Score=0;
  • Click the OK-button to save the expression.

The expression do just what it looks like: set the GameTime and Score variable to zero when the game starts.

AppState.OnUpdate is called once between every frame update, just like Model.OnUpdate. On our PlayingState we will add logic to spawn new walls and update the GameTime variable.

  • Add a ZExpression component to OnUpdate on PlayingState.
  • Click on the Expression property.
  • Write the following code in the expression custom text editor:

GameTime+=App.DeltaTime;

  • Click the OK-button to save the expression.

The expression just increase our gametime variable with the amount of time that has elapsed since last frame update.

  • Add a Timer component to OnUpdate on PlayingState.
  • Set the following properties:
    • Name = WallTimer
    • Comment = Emit walls
    • Interval = 1

The Timer -component executes the OnTimer list on a set interval, in this case 1 second.

  • Expand WallTimer to display the OnTimer list.
  • Add a SpawnModel component to OnTimer.
  • Set the following property:
    • Model = WallModel
  • Add a ZExpression component to OnTimer.
  • Click on the Expression property.
  • Write the following code in the expression custom text editor:
Score+=100;
WallTimer.Interval=1.5 - clamp(GameTime*0.02,0,1);
  • Click the OK-button to save the expression.

The first line of expression increase the score with 100. The second line sets a new interval for the next time the OnTimer list will be executed. The interval is decreased with GameTime to make walls appear faster over time.

The PlayerState is now finished. Let's move on and define TitleState.

  • Add a AppState component to States on App.
  • Set the following property:
    • Name = TitleState

Now for the update-behaviour. When the user press space on the keyboard, the game will begin.

  • Expand TitleState to display the list properties.
  • Add a KeyPress component to OnUpdate on TitleState.
  • Set the following property:
    • Keys = (press spacebar to type a single " " character)
  • Expand KeyPress to display the list properties.
  • Add a SetAppState component to OnPressed.
  • Set the following property:
    • State = PlayingState

OnRender on title state will display a message.

  • Add a UseMaterial component to OnRender on TitleState.
  • Set the following property:
    • Material = TextMaterial
  • Add a RenderText component to OnRender on TitleState.
  • Set the following properties:
    • Text = PRESS SPACE TO START
    • Scale = 0.75

Time to save the project if you haven't already done so. Select File - Save Project from the main menu. Name the project "OneButton".

Note: There is no Undo-functionality in ZGameEditor, so it is a good idea to save your work often! (but since version 1.9.6 you can "Undo delete" if you accidentally remove a component)

We need to tell ZGameEditor that the initial application state is the TitleState.

  • Add a SetAppState component to OnLoaded on App.
  • Set the following property:
    • State = TitleState

Collision detection and score-display

Almost done now. Three more things are needed:

  1. Define that we want to have collision detection between player and walls.
  2. Display the current score.
  3. Define what will happen when the player collides with a wall.

First the collision:

  • Add a DefineCollision component to OnLoaded on App.
  • Set the following properties:
    • Comment = Player vs Walls
    • Cat2 = 1

Then the score-display. We render the text from App.OnRender instead of PlayingState.OnRender because we want the score to be displayed in both state. That way, the last score will be visible on title screen.

  • Add a UseMaterial component to OnRender on App.
  • Set the following property:
    • Material = TextMaterial
  • Add a RenderText component to OnRender on App.
  • Set the following properties:
    • Comment = Display score
    • TextFloatRef = Score.Value
    • X = 0.62
    • Y = -0.87

The TextFloatRef property allows the value of a property on another component to be displayed as text.

Now for the definition of behaviour when the player collides with a wall. Clear screen of all models and return to the title screen:

  • Add a RemoveAllModels component to OnCollision on PlayerModel.
  • Add a SetAppState component to OnCollision on PlayerModel.
  • Set the following property:
    • State = TitleState

The finished game

That's it! First save your project again, and then try pressing F9 to start the game.

The finished tutorial source file can be downloaded here: OneButton.zip. The final version also have some simple sound effects added.

Some suggestions for improvements:

  • modify the difficulty to make it easier or harder
  • give the walls different heights
  • use a separate color for each wall

Green Marinee theme adapted by David Gilbert, powered by PmWiki