Making a Game - Showing Information - EP 18

by

Video Tutorial

Aims of this Tutorial

  • Share display names between clients via the server.
  • Create a panel to show the information of players.
  • Change the information panel dependant on whether the mouse is hovering over a player.

Sharing Display Names

In this section, it is important that we don't make variables and functions such as 'name' or 'set_name' as these are already taken by the parent class and we do not want to override them. First, we need to get an input from the player to find out what they want their name to be. This is very simple, we just need to create another LineEdit in the lobby scene and when we connect to the server, send that data to the server.

func _connected_to_server() -> void:
	self.console_print("Connected to server") # Let the server know all about us (eg name)
	rpc("set_display_name", peer.get_unique_id(), display_name)

We now need to use these rpc calls to generate a dictionary on the server where the unique ids are the keys (since we know they will be unique) and the names will be the values. When we start the game, we need to send this dictionary back. To avoid deserialisation, which can cause security risks, we will send the data back piecewise.

func send_name_dict():
	for element in name_dict.keys():
		rpc("add_name_to_dict", element, name_dict[element])

remote func set_display_name(id : int, display_name : String) -> void:
	name_dict[id] = display_name

The add_name_to_dict rpc call is the same as set_display_name but on the client. Now we have the names distributed between the clients!

Creating the Info Panel

On the client, in the player scene, we need to make a Panel node. We can then place it where we like. I put mine in the top right hand side. Now we need to make a Label node as a child to the panel. To avoid the text being right at the edge of the panel, we can give it margins of ±10 pixels. Once you are happy with this configuration, you can move onto the next step, which will be to populate this label with information.

Displaying the Information

First we need a way to find out the name of any player. To do this, every instance can find out their own name. We just need to make the arena pass on our id when we create the players using the init() function, and we can query the dictionary to find it.

func get_display_name() -> String:
	return get_parent().get_parent().name_dict[player_id]

Now we can write a helper function to turn a Vector3 into a String where each number is rounded to 1 decimal place. This cannot be done directly since there is only one rounding function that will round to an integer (as a float type). We therefore need to make use of multiplying and dividing by 10 to get what we need. Also note the use of \ to split up the lines and make the function more readable.

func get_human_readable_vector(vector : Vector3) -> String:
	return "(" + String(round(vector.x * 10)/10) + ", " + \
		String(round(vector.y * 10)/10) + ", " + \
		String(round(vector.z * 10)/10) + ")"

Now we can write a couple of functions to show this text and clear the text on the panel. We also set a member variable mouse_over_player to the hovered object so that we can update the text in the main loop while the plan is playing.

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 += "Attitude: Hostile"
	
	$InfoPanel/Label.text = text
	mouse_over_player = player_obj

func clear_info_panel() -> void:
	$InfoPanel/Label.text = ""
	mouse_over_player = null

Now we need to actually call these functions based on whether the mouse is over the player or not. We need to send this information via the arena to the local player who has control of the panels. To get the player who has the mouse, we need to use a couple of callback functions that are available to anything that derives from CollisionObject.

func _on_Player_mouse_entered():
	get_parent().mouse_enter(self)

func _on_Player_mouse_exited():
	get_parent().mouse_exit(self)

These two functions on the arena then tell the local player what to show. We get the local player by getting our uid from the tree and accessing the players dictionary.

func mouse_enter(player_obj : RigidBody):
	players[get_tree().get_network_unique_id()].display_data(player_obj)

func mouse_exit(player_obj : RigidBody):
	players[get_tree().get_network_unique_id()].clear_info_panel()

The final piece of the puzzle is to update our text while hovering.

func _process(delta : float) -> void:
	if mouse_over_player:
		display_data(mouse_over_player)