Making a Game - Displaying Health and Ending Game - EP 22
by Damien
Video Tutorial
Aims of this Tutorial
- Calculate health on client to show in info box.
- Find out the winner on the server and send it to the client.
- Have the client show the winner once the turn has ended.
Displaying Health
Since we already wrote the code for calculating the damage players take, we simply need to copy that code onto the client. In order to get the health at the start of each turn however, we will get the server to tell us how much health each player has. This will help keep us in sync if there is a slight discrepancy. On the server we need to add the following code.
# Player.gd - send_all_plans():
end_health = self.health
rpc("r_end_turn", time_of_death, end_translation, end_rotation, end_linear_velocity, end_rotational_velocity, end_health)
And then on the client we have:
# Player.gd - r_end_turn():
end_health = r_end_health
# Player.gd - play_full_plan():
health = 100.0
# Player.gd - play_current_plan():
health = end_health
We now need to display this health on the info card. This is trivial since we made a function to update the data.
func display_data(player_obj : RigidBody) -> void:
var text = "Name: " + player_obj.get_display_name() + "\n"
text += "Position: " + get_human_readable_vector(player_obj.translation) + "\n"
text += "Rotation: " + get_human_readable_vector(player_obj.rotation_degrees) + "\n"
text += "Hull Integrity: " + String(player_obj.health) + "%\n"
$InfoPanel/Label.text = text
mouse_over_player = player_obj
Find out the Winner on the Server
In order to find out the winner, we can get every player to report their death to the arena, which will then check how many people have died and end the game appropriately. There will then be only one player left alive, who will be the winner. The players death function is now the following.
func die(time : float) -> void:
is_alive = false
if !has_died:
if report_death_to_arena():
finish_game()
has_died = true
$CollisionShape.disabled = true
time_of_death = time
func report_death_to_arena() -> bool:
return get_parent().register_death()
func finish_game() -> void:
get_parent().finish_game()
The arena will return true if there is only one player left, which will get the arena to end the game. Note the order in which we set is_alive
and has_died
. This is important because, when we find out the winner, we must not be is_alive
, but we cannot has_died
because then we would fail the first if statement. The complementary code on the arena is as follows.
func register_death() -> bool:
deaths += 1
if deaths == len(players.keys()) -1:
return true
return false
func finish_game() -> void:
for player in players.values():
if player.is_alive:
declare_winner(player)
func declare_winner(player) -> void:
var name = get_parent().name_dict[player.id]
rpc("declare_winner", name)
Here we are checking our deaths counter (starts at 0), and if there are 1 less than the number of players, we have a winner. We then find the only player who is alive by looping through our dictionary, find their name, and announce it to all the clients.
Announcing Winner
If we were to simply announce the winner immediately, we would show the winner before we even play the plan. We therefore need to delay the announcement until after we have finished playing the plan. We need a new boolean to keep track of this for us, was_plan_running
, which lags behind running_plan
by one frame.
# Player.gd _physics_update():
if !running_plan:
if is_local and was_running_plan:
get_parent().show_winner()
was_running_plan = false
return
was_running_plan = true
On the arena we have both the remote method the server calls and the show_winner
the local player calls.
remote func declare_winner(name : String) -> void:
winner_name = "Victor: " + name
$WinnerPanel/WinnerLabel.text = winner_name
func show_winner() -> void:
if winner_name != null:
$WinnerPanel.visible = true
The default value of winner_name
is simply null
. This concludes this week's tutorial. Thank you all for sticking with me over this hiatus!