Making a Game - Displaying Health and Ending Game - EP 22

by

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!