Skip to main content

First-Person Shooter: Start and end of the match

info

This is Elympics First-Person Shooter tutorial: part 11. In this part we’ll expand controling the gameplay by adding start and end of the match. See: Part 10.

Once you have a script that controls the points scored by your players, you can write a suitable controller that will display a summary screen informing all clients about the result of the match.

States of the game

To make the gameplay even smoother, you can also add a start screen that will display the time remaining until the start of the game.

The implementation of the above assumptions will require dividing the game into three states:

  • Initiation of the match;
  • Match proper (Gameplay);
  • End of the match.

During Match Initiation, players will see the start screen. Once the countdown to the start is over, the game will change its state to the proper match in which clients can control their players and fight among themselves. When one of the players scores the number of points required to win the game, the state of the game will change to the last state: End of the game. In this state, you should display a screen informing your clients that the match is over and who turned out to be the winner.

You can describe the current state of the game by creating the appropriate GameState enum:

public enum GameState
{
Prematch = 0,
GameplayMatchRunning,
MatchEnded
}

Game state controller

Then, you can create a controller that will manage the current state of the game:

public class GameStateController : ElympicsMonoBehaviour, IInitializable
{
[Header("References:")]
[SerializeField] private PlayerScoresManager playerScoresManager = null;

public ElympicsInt CurrentGameState { get; } = new ElympicsInt((int)GameState.Prematch);

public void Initialize()
{

}

private void ChangeGameState(GameState newGameState)
{
CurrentGameState.Value = (int)newGameState;
}

public void ElympicsUpdate()
{
if (playerScoresManager.WinnerPlayerId >= 0 && (GameState)CurrentGameState.Value == GameState.GameplayMatchRunning)
{
ChangeGameState(GameState.MatchEnded);
}
}
}

The game starts with a Prematch state by default. Thanks to the previously written PlayerScoresManager script, when the winner is selected, the game status changes to MatchEnded. However, the question of when to change the state of the game to GameplayMatchRunning remains unresolved.

Game initializer

You’d like the game to count down a certain amount of time before the start of the match so that all players have a chance to prepare for it. For this purpose, you’d have to create another class: GameInitializer. Its only task will be to count down to zero immediately after starting the game and to trigger appropriate feedback.

public class GameInitializer : ElympicsMonoBehaviour, IUpdatable
{
[SerializeField] private float timeToStartMatch = 5.0f;

public ElympicsFloat CurrentTimeToStartMatch { get; } = new ElympicsFloat(0.0f);

private ElympicsBool gameInitializationEnabled = new ElympicsBool(false);

private Action OnMatchInitializedAssignedCallback = null;

public void InitializeMatch(Action OnMatchInitializedCallback)
{
OnMatchInitializedAssignedCallback = OnMatchInitializedCallback;

CurrentTimeToStartMatch.Value = timeToStartMatch;
gameInitializationEnabled.Value = true;
}

public void ElympicsUpdate()
{
if (gameInitializationEnabled)
{
CurrentTimeToStartMatch.Value -= Elympics.TickDuration;

if (CurrentTimeToStartMatch <= 0.0f)
{
OnMatchInitializedAssignedCallback?.Invoke();

gameInitializationEnabled.Value = false;
}
}
}
}

In the editor, you specify how many seconds the countdown should last. You also provide an appropriate ElympicsFloat that stores the current countdown time. You’ll be able to subscribe to the appropriate methods responsible for displaying the time on the start screen to it. This class also has an appropriate ElympicsBool flag thanks to which it stops modifying the timer once the requested limit is reached. You also store the appropriate callback passed with the InitializeMatch method to call it when the timer reaches a certain value.

The InitializeMatch method is used to start the countdown. To do this, the gameInitializationEnabled flag is set to true and the passed callback is stored.

In the ElympicsUpdate method, if the countdown flag is true, you modify the variable responsible for storing the current timer with the Elympics.TickDuration value. If the timer counts down the specified time (its value will be less than or equal to 0), the stored callback is called and the value of the flag is changed to false again.

Updating the game state controller

With the GameInitializer prepared this way, you can return to your GameStateController and complete it:

public class GameStateController : ElympicsMonoBehaviour, IInitializable
{
[Header("References:")]
[SerializeField] private GameInitializer gameInitializer = null;
[SerializeField] private PlayerScoresManager playerScoresManager = null;

public ElympicsInt CurrentGameState { get; } = new ElympicsInt((int)GameState.Prematch);

public void Initialize()
{
gameInitializer.InitializeMatch(() => ChangeGameState(GameState.GameplayMatchRunning));
}

private void ChangeGameState(GameState newGameState)
{
CurrentGameState.Value = (int)newGameState;
}

public void ElympicsUpdate()
{
if (playerScoresManager.WinnerPlayerId >= 0 && (GameState)CurrentGameState.Value == GameState.GameplayMatchRunning)
{
ChangeGameState(GameState.MatchEnded);
}
}
}

You add a reference to GameInitializer.cs and, in the Elympics Initialize method, call the method InitializeMatch() from the GameInitializer class, where you provide a function that modifies the current state of the game to GameplayMatchRunning as a callback.

We can now place the prepared scripts in the scene:

First-Person Shooter

3... 2... 1...

From now on, the match will start in the Prematch state. Then, after 5 seconds, it will go to the proper GameplayMatchRunning state. When one of the players reaches the designated number of points, the state of the game will be changed to MatchEnded.

First-Person Shooter

5 seconds to start, as promised!

In the next part we'll create start and summary screen at the end of the game. 📢