Module 4 - Broadcast Events
As the functionality grows, other parts of our game will need to know when the game state has changed. Our GameManager script could keep track of all components that must be updated about state changes and then update those components directly.
Retaining a reference in GameManager to all other managers and systems and serving as a proxy for all updates is a common and acceptable strategy. However, that can quickly become difficult to manage. We are going to do something different. When our game state changes, we are going to emit a broadcast event, to which any component can subscribe.
Create GameStateChanged Local event to broadcast
First, we must create the event to broadcast. When creating events, you can declare any parameters that should be passed along with the broadcast. In this case, we want to pass our updated game state.
At the top of your GameManager file, below the import statements. add the following:
export const gameStateChanged = new hz.LocalEvent<{state: GameState}>(
'gameStateChanged');
The above defines the event to be a local event that passes along a value called state of type GameState, and includes a message: gameStateChanged. It is up to the subscriber to respond to it.
To emit (publish) this event, we use the built-in sendLocalBroadcastEvent function to pass the current value of this.gameState. Add the following line inside of our setGameState function, at the very bottom:
this.sendLocalBroadcastEvent(gameStateChanged, {state: this.gameState});
Later, we create components to connect to this event.
Note : There are no components listening for this event now, and this will cause console warnings. It’s ok for now.
Create setGameState Local event and broadcast
We also want to allow other components to send in state change requests. For that, our Game Manager should listen for broadcast events.
Create another new event at the top of the GameManager file to pass a GameState value:
export const setGameState = new hz.LocalEvent<{state: GameState}>('setGameState');
Inside of the start function, connect (subscribe) to this event using the connectLocalBroadcastEvent function. When this event is received, call our setGameState function with the state that was passed:
this.connectLocalBroadcastEvent(setGameState, (data: {state: GameState}) => {
this.setGameState(data.state);
});
At this point, my GameManager file looks like this:
import * as hz from 'horizon/core';
export const gameStateChanged = new hz.LocalEvent<{state: GameState}>('gameStateChanged');
export const setGameState = new hz.LocalEvent<{state: GameState}>('setGameState');
class GameManager extends hz.Component<typeof GameManager> {
static propsDefinition = {};
private gameState!: GameState;
start() {
this.setGameState(GameState.Ready);
this.connectLocalBroadcastEvent(setGameState, (data: {state: GameState}) => {
this.setGameState(data.state);
});
}
public setGameState(state: GameState): void {
if (this.gameState === state) {
return;
}
switch (state) {
case GameState.Ready:
if (this.gameState !== GameState.Playing) {
this.gameState = GameState.Ready;
}
break;
case GameState.Playing:
if (this.gameState === GameState.Ready) {
this.gameState = GameState.Playing;
}
break;
case GameState.Finished:
this.gameState = GameState.Finished;
break;
} // console.log(`new game state is: ${GameState[this.gameState]}`);
this.sendLocalBroadcastEvent(gameStateChanged, {state: this.gameState});
}
}
hz.Component.register(GameManager);
export enum GameState {
'Ready',
'Playing',
'Finished',
}
We now have a simple yet flexible game state manager!
What you have at this checkpoint can be saved and reused in many games. We have a class that broadcasts an event any time our game state changes. This is a very useful, basic Game Manager setup that is used often.
In this and the previous module, you:
- Created events:
- Local event to track game state
- Local event to track when state changes
- Broadcast event to send messages when state changes
In the next module, you use PlayerManager and GameManager to build the gameplay in your game.