Let's Make A Multiplayer Game #8: Damage And Revive
Today we will make the clients able to attack other clients. Currently, you can damage the other players, but the other players don’t know about it. Let’s start implementing the die function.
Damage other players
Let’s check the BaseCharacter
script. In a previous tutorial we made a die function in this script. This function will be called when the hp
of the character is less or equal than 0. Instead of implementing the die function here, we will override it in the Player
script.
In the player script, override the die
function and, inside it, set the state to DEAD
. We have to do it in the player script because the BaseCharacter
does not have state machine. Besides, it’s not necessary to make the BaseCharacter
play the dead animation, it will be played automatically thanks to the animation_changed
signal we made in the last tutorial.
Next, let’s tell the other players when we damage them. Add a signal called damage with 2 arguments: the id of the damaged player and the amount of damage. We will emit this signal from the hitbox script.
Open it, and, below the line where we damage the player, emit the damage signal we just created. To get the id of the damaged player, cast his name to int
. As for the damage amount, type 1. I used the name of the character to get his id, but he does not have his id as name, we have to implement this before continuing.
But before, now that I think about it, we don’t need to call the damage
function here. Each client will manage his own hp
, we only have to tell them when we damage them, we don’t need to reduce the hp
from his BaseCharacter
.
Open the BaseCharacter
script and add a function called initialize
with 3 parameters:
- his id,
- his name, and
- his character index.
Also, create an onready variable for the label name, we will need it in a moment. Inside the function, change the name of the character node to his id. The name of the node must be a String
, that’s why we have to convert it.
Next, set the text of the label with the name
parameter. This way, the label will show the real name of the player. This will make more sense when we implement the character creator and we can change the name.
We have to call this function when we spawn the players. In the pre_configure_game
function, after creating a new instance of our player, call his initialize
function. I don’t remember why I used call_deferred
, I think I was getting some error calling the function normally. We can get the id of the player with the get_network_unique_id
function. The name and character index are in the my_info
dictionary. The character index will be useful in the future.
Now, let’s do the same for the rest of the players. In the for loop, after creating a new instance of the BaseCharacter
scene, call his initialize
function. The id of the player is player_id
. We can get the name and the character index from the player_info
dictionary.
If we play the game now, the name of the players will be the default name we defined in the my_info
dictionary instead of the default text of the Label
. It will be “Elizabeth” instead of “Hasegawa”.
With that done, let’s continue with the damage
signal. Like the other player signals, connect it at the pre_configure_game
function. Use _on_player_damaged
as the target function, and print an error message if the connection fails.
Create the _on_player_damaged function. Inside the function, call the damage_player function of the server. Don’t forget to pass the function parameters as arguments.
In the server project, add the damage_player
function. This one is different of the ones we implemented in the last video. Instead of telling all the player in the room that a player has been damaged, we will only tell the one who has been damaged. Since there is no visual representation of the health, the other players don’t need to know that we damaged the player.
So, inside the function, call the damage
function of the player who has been damaged. Pass only the damage
as argument.
Finally, in the client script, add the damage
function. Call the damage
function of our player, passing the damage
parameter as argument. You can get the player of the client using the get_network_unique_id
function as the key of the player_info
dictionary.
Let’s open some clients to test it.
By the way, to open multiple clients, I execute godot in the project directory.
Now, when we attack the other player, the damage function of his client script is executed and his life is reduced. When it reaches 0, he dies. Thanks to the can_move
variable we made in a previous video, he cannot move after dying.
After playing the game, we get 2 warnings:
The first one is because we are not using the character index parameter in the initialize
function. We will use it in a later tutorial, just ignore the warning for the moment. We also get a warning because we are not emitting the damage
signal from the Player
script. Since we are emitting the signal from the hitbox script, we don’t care about the warning, we can remove it clicking on ignore. This warning will not bother us anymore.
Revive players
Now, let’s make the player resurrect with a timer. In the Player
scene, add a new Timer
node and change his name to “ReviveTimer”. The wait time of the timer is the time it will take to revive the player. I am going to use 7, so we don’t have to wait much. We don’t want the timer to loop, so, check one shot.
Actually, the dead animation is very long, I’m going to cut it to 5 seconds, so the player does not revive before the dead animation finishes.
Enter the state machine script and add an onready variable for the ReviveTimer
.
When the player enters the DEAD
state, start the timer.
Now, we need a way to go back to IDLE
when the timer ends. In the _get_transition
function, add a DEAD
case. If the ReviveTimer
is stopped, return IDLE
.
So, after 7 seconds, the player will change the state back to IDLE
. But he will still have 0 hp
and can_move
will still be false
, we have to change them when we exit the DEAD
state.
Create a new function called _exit_state
with a parameter containing the state we exited. If the state exited is DEAD
, change the hp
back to 2 and can_move
to true
. In the set_state
function, call the _exit_state
function after making sure the new state is different, before updating the value. Pass state
as argument. At this point, state
still have the previous value because it has not been updated yet.
If we play the game now, the players should revive 7 seconds after dying. As we can see, they are indeed, resurrecting 7 seconds after dying.
That’s all for today. At this point we already have a playable game, but all the players have the same character and name. In the next tutorial, we will make a character creator.