!!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:

Path:/images/tut3/pic1.png

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 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.

Path:/images/tut3/pic2.jpg

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'''

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'''

Path:/images/tut3/pic3.png

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 [[ComponentRef.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 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:
#Decrease the Y-velocity: This gives a downward accelleration which simulates gravitation.
#Test that velocity do not get too high.
#Test that the player do not move outside the screen.
#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:

#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.
#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'''

Path:/images/tut3/pic4.png

!!!! 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.

Path:/images/tut3/pic5.jpg

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:

Path:/images/tut3/pic6.png

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 [[ComponentRef.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 [[ComponentRef.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.

Path:/images/tut3/pic7.png

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'''

Path:/images/tut3/pic8.png

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!

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:
#Define that we want to have collision detection between player and walls.
#Display the current score.
#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.

Path:/images/tut3/pic9.png

The finished tutorial source file can be downloaded here: [[Path:/files/zzdc/OneButton.zip | OneButton.zip]]. The final version also have some simple sound effects added.

Some suggestions for improvments:
*adjusting difficulty
*making the walls of different heights
*different colors of each wall