User interfaces, game mechanics and sounds are fundamental in every game.
We will learn how to create a simple 2D dice game called Dice Pig in Godot Engine.
At first, let's take a look at the game itself. Dice Pig has some simple game rules that anyone can learn in a snap:
You play as the pig on the left. Battle against 9 individual animals (rat, cat, rabbit, dog, monkey, bear, cow and two specials) and get a high score - but keep in mind that you only have 3 lives. (Win extra life by playing risky)
You can download and play the final game for free (Windows/macOS/Linux) to get an idea how your game can look like, or just watch it here:
The tutorials will help you create your own dice game from scratch. You can follow the steps, make adjustments and customize that way your game.
If you get lost in the tutorial, you can always go to Github. There you can download and try the latest state of the game. At the end of each tutorial step you will find a link to the related commit in the repository.
In this tutorial you will learn...
At the end of this tutorial you will have a basic game that will look like:
Requirements
If you need help or more information, you can dip into the documentation of Godot Engine and leave a comment under this tutorial. Feedback is welcome!
If you start it the first time you will get a dialog that asks you to open the Open Asset Library. Just Cancel the dialog and create a New Project by clicking the button in the right section of the Project Manager window.
Next, in the Create New Project dialog set the Project Name to Dice-Pig and select the Project Path to a folder where you want to store your game. Click on Create folder and finish the dialog with Create & Edit.
If you need help with the first steps, simply watch this video tutorial. You are ready now to set up the game scene.
Switch to 2D. You can zoom out and adjust it for a better overview:
Note: On the top of the window you can easily switch between the workspaces (2D, 3D, Script and AssetLib).
Now, we need a main scene for our game: Click on the + in the Scene dock (right upper corner) to add a new Node:
This opens the Create New Node dialog. Select Node and click on Create. Node is the base class for all scene objects. This will be our main scene. Double-click on the created Node and rename it to Game.
Go into the Scene menu and select Save Scene. A dialog will ask for a name, keep Game.tscn and click Save. Play the project by pressing F5 on your keyboard - or use the play button in the right upper corner. On first project start a dialog will appear to Select a main scene. Confirm and Open Game.tscn. The game window will open and run, please close it afterwards.
On the left side you have the FileSystem dock, where you will manage your project files and assets. Download the asset package for Dice Pig and unzip it into your project folder. The assets folder contains fonts, images and sounds.
Godot Engine will automatically update the FileSystem dock:
Optional: Download the Dice Pig icons and unzip them also into the project folder to replace the existing icons.
Finally, we need to adjust settings. Open the Project menu and select Project Settings. Find and set following preferences:
Here is a hierarchical list of Nodes (and specific types in brackets) we need for our game:
When you finished adding the Nodes as shown in the screenshot, you can adjust the properties in the Inspector dock. But first Save your project. Select a Node in the Scene and set its values in the Inspector. We will start with the Background:
Game/Background:
Game/DiceButton:
Game/Turn/Left:
Game/Player1/Image:
Save the project again. (Github commit)
This will generates a default script:
extends Node
# class member variables go here, for example:
# var a = 2
# var b = "textvar"
func _ready():
# Called every time the node is added to the scene.
# Initialization here
pass
#func _process(delta):
# # Called every frame. Delta is time since last frame.
# # Update game logic here.
# pass
Godot Engine uses a script language called GDScript. There is a good introduction in the documentation you should read first.
Delete the comments starting with #, so that game script shrinks to:
extends Node
func _ready():
pass
Take care of the TAB before the keyword pass. GDScript uses tab-based indentation. Read more about it in the documentation linked above.
Let's take a look how to connect the DiceButton to the game script:
Note: In the Connection Signal dialog you can choose where and how a signal should be handled. First of, we keep the method name, select Game for our script and enable Make Function, so that a new function will be automatically created in the script and linked to the signal. (default)
Connect the Turn/Left and Turn/Right buttons the same way. We will end up with a game script that looks like:
extends Node
func _ready():
pass
func _on_DiceButton_pressed():
pass # replace with function body
func _on_Left_pressed():
pass # replace with function body
func _on_Right_pressed():
pass # replace with function body
Delete the comments again and remove the empty (double) lines:
extends Node
func _ready():
pass
func _on_DiceButton_pressed():
pass
func _on_Left_pressed():
pass
func _on_Right_pressed():
pass
extends Node
var dice = 0 # 0 - 5
var turnPoints = 0 # 0 - 100
var player1Points = 0 # 0 - 100
var player2Points = 0 # 0 - 100
var currentPlayer = 0 # 0 - 1
const colorActive = Color("#ffffff")
const colorInactive = Color("#bbbbbb")
We added some variables and constants that will represent the current state of our game. The comment defines the range of that variable, for example: if dice is 0 the actual dice in the UI will show a one, so if dice is 5 we will see six.
Turn and player points will start from 0 and go to 100. The variable currentPlayer will represent the sequence-player, 0 stands for Player1 and 1 for Player2. It's a little bit tricky, because we always start counting from 0.
There are two color constants that will be used to change the color of the player points and arrows.
func _ready():
randomize()
resetTurnPoints()
resetPlayerPoints()
setCurrentPlayer(0)
func resetTurnPoints():
turnPoints = 0
$Turn/Points.text = str(0)
func resetPlayerPoints():
player1Points = 0
$Player1/Points.text = str(0)
player2Points = 0
$Player2/Points.text = str(0)
The _ready() function will be called once then the game starts. We have to call the randomize() function first, so that the game gets fresh random numbers for our dice. Then we reset the game state calling the two reset functions and set current player to Player1.
Add the two reset functions resetTurnPoints() and resetPlayerPoints() to your script.
func setCurrentPlayer(player):
currentPlayer = player
if currentPlayer == 0:
$Player1/Points.modulate = colorActive
$Player2/Points.modulate = colorInactive
$Turn/Left.modulate = colorActive
$Turn/Left.disabled = false
$Turn/Right.modulate = colorInactive
$Turn/Right.disabled = true
else:
$Player1/Points.modulate = colorInactive
$Player2/Points.modulate = colorActive
$Turn/Left.modulate = colorInactive
$Turn/Left.disabled = true
$Turn/Right.modulate = colorActive
$Turn/Right.disabled = false
func setNextPlayer():
resetTurnPoints()
if currentPlayer == 0:
setCurrentPlayer(1)
else:
setCurrentPlayer(0)
The setCurrentPlayer() function is used to update the internal variable currentPlayer and to change the player UI: We change modulate to colorInactive and colorActive and enable/disable the Left or Right buttons. The setNextPlayer() function will switch between the two players.
func _on_DiceButton_pressed():
dice = randi() % 6
$Dice.frame = dice
if dice == 0:
setNextPlayer()
return
turnPoints += dice + 1
$Turn/Points.text = str(turnPoints)
This is the signal function for the dice button. randi() will give use a random number and with modulo 6 the result will be within the range of 0 to 5. Dice is a Sprite with the animation from 0 to 5. If dice is 0, we will show the frame 0 with one and if dice is 5, then we will show frame 5 with six:
If the dice is 0 (red one) the current player lose the turn points and the next player can go on. The return will end the function. Otherwise the dice points will be added to the turn points and the current player can roll again, or...
func _on_Left_pressed():
if turnPoints == 0:
return
player1Points += turnPoints
$Player1/Points.text = str(player1Points)
setNextPlayer()
func _on_Right_pressed():
if turnPoints == 0:
return
player2Points += turnPoints
$Player2/Points.text = str(player2Points)
setNextPlayer()
...the current player can press either the Left or Right button to 'hold' the turn points and add them to the player points. At the end of the two function we will call the function setNextPlayer().
Note: Make sure you use TAB and no spaces for indentation. Coping and pasting the above code lines can result in spaces. If you ran into errors, make sure the script code is correct and fix spaces manually.
Save the script again. (Github commit)
It's time to play and test the game. Press F5 to run the game. Check if the mechanics works as expected.
There is one thing left to do...
Now, set the streams in the Inspector:
To play the sounds, add the code lines in the game script.
func _on_DiceButton_pressed():
dice = randi() % 6
$Dice.frame = dice
if dice == 0:
$Sound/Pig.play() # <-- play pig sound
setNextPlayer()
return
# play dice sounds:
var diceType = randi() % 4
if diceType == 0:
$Sound/Dice1.play()
if diceType == 1:
$Sound/Dice2.play()
if diceType == 2:
$Sound/Dice3.play()
if diceType == 3:
$Sound/Dice4.play()
turnPoints += dice + 1
$Turn/Points.text = str(turnPoints)
func _on_Left_pressed():
if roundPoints == 0:
return
$Sound/Hold.play() # <-- play hold sound
player1Points += roundPoints
$Player1/Points.text = str(player1Points)
setNextPlayer()
func _on_Right_pressed():
if roundPoints == 0:
return
$Sound/Hold.play() # <-- play hold sound
player2Points += roundPoints
$Player2/Points.text = str(player2Points)
setNextPlayer()
Save the script again. (Github commit)
Congrats, we completed this tutorial. Now it's time to play your dice game.
There are some things that are missing: High score and Game over
But what about a computer opponent or the game menu?
Feel free to implement it by yourself, or stay tuned for next tutorials when we add new features.