Making a Game - Setting Colours and Snapping Camera - EP 20


Video Tutorial

Aims of this Tutorial

  • Allow the camera to snap between players for easy navigation.
  • Allow the player to pick their colour.
  • Share this colour via the server and use it change the colour of the players on all clients.

Camera Snapping

This is a fairly simple thing to do in theory, but refining the parameters can be a little difficult. We will be working on the camera script (obviously), and we will need to get a list of players which we can snap to and a function to snap between them. I did some fine tuning of the numbers for my game, but you may need to adjust them for yours.

func move_to_player(index : int):
	translation = players[index].translation
	rotation = players[index].rotation
	translate(Vector3(0, -0.8, -2))
	rotate_object_local(Vector3(1,0,0), PI-0.3)

To manage calling this function, we will use the physics loop (like we did before). We will use a couple variables to help keep track of which player to look at and whether we are still tethered to a player. Our new update loop will be:

var index : int = 0
var locked : bool = false

func _physics_process(delta : float) -> void:
	if Input.is_action_pressed("camera_forward"):
		translate(TRANSLATION_SPEED * Vector3(0, 0, -1)); locked = false
	if Input.is_action_pressed("camera_backward"):
		translate(TRANSLATION_SPEED * Vector3(0, 0, 1)); locked = false
	if Input.is_action_pressed("camera_strafe_up"):
		translate(TRANSLATION_SPEED * Vector3(0, 1, 0)); locked = false
	if Input.is_action_pressed("camera_strafe_down"):
		translate(TRANSLATION_SPEED * Vector3(0, -1, 0)); locked = false
	if Input.is_action_pressed("camera_strafe_right"):
		translate(TRANSLATION_SPEED * Vector3(1, 0, 0)); locked = false
	if Input.is_action_pressed("camera_strafe_left"):
		translate(TRANSLATION_SPEED * Vector3(-1, 0, 0)); locked = false
	if Input.is_action_pressed("camera_pitch_down"):
		rotate_object_local(Vector3(1,0,0), -ROTATION_SPEED); locked = false
	if Input.is_action_pressed("camera_pitch_up"):
		rotate_object_local(Vector3(1,0,0), ROTATION_SPEED); locked = false
	if Input.is_action_pressed("camera_roll_left"):
		rotate_object_local(Vector3(0,0,1), ROTATION_SPEED); locked = false
	if Input.is_action_pressed("camera_roll_right"):
		rotate_object_local(Vector3(0,0,1), -ROTATION_SPEED); locked = false
	if Input.is_action_pressed("camera_yaw_left"):
		rotate_object_local(Vector3(0,1,0), ROTATION_SPEED); locked = false
	if Input.is_action_pressed("camera_yaw_right"):
		rotate_object_local(Vector3(0,1,0), -ROTATION_SPEED); locked = false
	if Input.is_action_just_pressed("camera_switch_player"):
		move_to_player(index % len(players))
		index += 1
		locked = true
	if locked:
		move_to_player(index % len(players))

Now the last thing to do is get this list of players when we create the camera. This is quite simple, as we simply need the arena to call a function to add each player to the list on loading them in. The modifications required are given below. 


var players = []

func register_player(player):

Setting Colours

Note that my code uses the American spelling of the word 'colour' rather than the British version since the Godot uses that version internally and I am not going to recompile Godot with that one change. In order to pick the colour, we can use a ColorPicker node in the lobby. Then on the root node, we need to make the same changes we did for the names. On the client we have:

# _on_StartButton_pressed()

player_color = $Lobby/ColorPicker.color

# _connected_to_server()

rpc("set_player_color", peer.get_unique_id(), player_color)

remote func add_color_to_dict(id : int, color : Color):
	color_dict[id] = color

And on the server:

func send_color_dict() -> void:
	for element in color_dict.keys():
		rpc("add_color_to_dict", element, color_dict[element])

remote func set_player_color(id : int, color : Color) -> void:
	console_print("Setting color of " + String(id) + " to " + String(color))
	color_dict[id] = color

# start_game()


Displaying Colours

In order to display the colours on the players, we need to have a copy of the materials saved in our project folder. I saved mine in a sub-directory called 'Shaders'. Then before we can assign them to the players, we need to duplicate them so that we are not overwriting the data. We can get the arena to pass on their colour in the init function and then the init function can call this new function.

func set_colors(color : Color):
	var insideMaterial : Material = preload("res://Shaders/InsideSpaceship.tres").duplicate()
	insideMaterial.emission = color
	var outsideMaterial : Material = preload("res://Shaders/OutsideSpaceship.material").duplicate()
	outsideMaterial.emission = color
	outsideMaterial.albedo_color = color
	var particleMaterial : Material = preload("res://Shaders/ParticleSpaceship.tres").duplicate()
	particleMaterial.emission = color
	for child in $EngineParticleContainer.get_children():
		child.draw_pass_1.material = particleMaterial
	for child in $spaceship.get_children():
		if == "Cube":
			child.set_surface_material(0, insideMaterial)
			child.set_surface_material(0, outsideMaterial)

To walk through this function, we first load in duplicates of the materials and alter their emission and albedo colours. Now we set the particle materials to the new material. We then get all the parts of the spaceship, and note that the part called "Cube" needs a different material. We can then set those materials accordingly.