07.scoreboard-adding-score-to-form

Adding score to the scoreboard form

In this lesson, we will add a score field to our form and scoreboard.

We will first update the scoreboard to accept a name and a score in each row.

We will create a new scene representing one score line to achieve that.

We will then add a score field to the form and send both the player name and the score to the scoreboard when pressing the OkButton.

Creating the ScoreLine scene

We will first design a scene to represent a row.

It should be a row with a Label for the score and another for the player’s name.

Create a new scene and click the Other Node button to create an HBoxContainer.

Rename the node to ScoreLine and add two Label nodes as its children.

Rename the Label nodes to PointsLabel and NameLabel respectively to distinguish them.

We can tint the score in yellow to distinguish it from the player’s name.

First, we want to add some placeholder text to each label. To do so, select each node and in the Inspector, write something in its Text field.

Then, select the PointsLabel. In the Inspector, scroll down to the Visibility category below the CanvasItem node section.

Expand the category and change the Modulate color to yellow.

Save the scene in the Scoreboard/ directory and attach a script to the ScoreLine node by right-clicking on it and selecting Attach Script.

We define two functions that we’ll use as an interface for the Scoreboard to display points and a name.

extends HBoxContainer

# We first get the two label nodes and store them with onready variables.
onready var points_label := $PointsLabel
onready var name_label := $NameLabel


# For each of the labels, we define a function that we will call from the
# scoreboard.
func set_player_points(points_value: String) -> void:
    # The function String.pad_zeros() adds zeros in front of the score to make
    # every score the same size. In this case, 6 digits.
    points_label.text = points_value.pad_zeros(6)


func set_player_name(player_name: String) -> void:
    name_label.text = player_name

Both functions are similar: they update the labels’ text.

Defining and using functions provides a safe way to use your scenes compared to directly accessing nodes like points_label.

It allows you later add instructions inside the functions without causing errors in the game.

Using the new ScoreLine from the Scoreboard

Open the Scoreboard script and replace the add_line() function with the following code. We will break it down in a moment.

# Note the addition of a new parameter: player points.
func add_line(player_name: String, player_points: String) -> void:
    # Loads the ScoreLine scene and creates a new instance of it.
    var line := preload("ScoreLine.tscn").instance()
    # We can add the ScoreLine instance as a child of the ScoresColumn like
    # plain nodes.
    scores_column.add_child(line)
    # As we instanced the ScoreLine scene, we can call the functions we defined
    # on it.
    line.set_player_name(player_name)
    line.set_player_points(player_points)

In the first line, we do two things:

  1. Preload the ScoreLine scene.
  2. Create a new instance and store that in the line variable.

The preload() function allows you to load a resource file, like a scene, using an absolute or a relative path.

It’s a special function that tells Godot to load the scene once upon launching the game and later keep it in memory. That way, we can quickly create new instances of it.

Like before, we add the line as a child of the ScoresColumn. In that, a scene instance acts a bit like a node: you can add it as a child of another node.

The last two lines call the two functions we defined in the ScoreLine scene and pass the player name and score, respectively.

Adding a points field to the ScoreForm

Now, we will add a points field to our ScoreForm.

Open the ScoreForm scene and duplicate the NameField node. Rename the new node to PointsField.

In the Inspector, we can remove the Placeholder -> Text.

In the script, we need to get our point field in a new onready variable. Around the top, add the variable.

onready var points_field := $HBoxContainer/PointsField

Then, we need to update our signal callback function.

func _on_OkButton_pressed() -> void:
    # ...
    scoreboard.add_line(name_field.text, points_field.text)
    # ...

We update the call to the Scoreboard.add_line() function and pass it the PointField’s text.

Currently, the player can add a new name and score to the scoreboard even if the fields are empty.

We can use a condition at the start of the function to prevent that.

func _on_OkButton_pressed() -> void:
    # If either field contains no text, we stop the function.
    if not name_field.text or not points_field.text:
        return
    # ...

Notice that we don’t write anything after the return keyword in the code listing above. Writing only return ends the function’s execution and does not return any value.

This code change completes the lesson. You can now run the scene, enter a name and a score, and it should appear on the scoreboard.

Limits of our code

Technically, you can type any text in the score field.

We don’t bother to ensure that you type a number here as, in a complete game, the score would be calculated for the player.

Practice: the inventory grid

Open the practice The inventory grid.

Once you complete the practice, the running scene should look like this.

Practice: the poem

Open the practice The poem.

The running scene should look like this.

Challenge: the to-do list

Open the practice The to-do list.

The final result should look like this.

The code

Here is the complete code for the scenes so far.

ScoreLine.gd

extends HBoxContainer

# We first get the two label nodes and store them with onready variables.
onready var points_label := $PointsLabel
onready var name_label := $NameLabel


# For each of the labels, we define a function that we will call from the
# scoreboard.
func set_player_points(points_value: String) -> void:
    # The function String.pad_zeros() adds zeros in front of the score to make
    # every score the same size. In this case, 6 digits.
    points_label.text = points_value.pad_zeros(6)


func set_player_name(player_name: String) -> void:
    name_label.text = player_name

Scoreboard.gd

extends PanelContainer

onready var scores_column := $MarginContainer/VBoxContainer/ScoresColumn


# Note the addition of a new parameter: player points.
func add_line(player_name: String, player_points: String) -> void:
    # Loads the ScoreLine scene and creates a new instance of it.
    var line := preload("ScoreLine.tscn").instance()
    # We can add the ScoreLine instance as a child of the ScoresColumn like
    # plain nodes.
    scores_column.add_child(line)
    # As we instanced the ScoreLine scene, we can call the functions we defined
    # on it.
    line.set_player_name(player_name)
    line.set_player_points(player_points)


func _on_HideButton_pressed() -> void:
    hide()

ScoreForm.gd

extends Control

onready var scoreboard := $Scoreboard
onready var name_field := $HBoxContainer/NameField
onready var points_field := $HBoxContainer/PointsField


func _on_OkButton_pressed() -> void:
    # If either field contains no text, we stop the function.
    if not name_field.text or not points_field.text:
        return

    scoreboard.show()
    scoreboard.add_line(name_field.text, points_field.text)
    name_field.text = ""