-
Notifications
You must be signed in to change notification settings - Fork 32
Game Events
Great game architecture is decoupled. If there was a golden rule for game code architecture in Godot, that should be "Scene must work on their own". A game designer that knows nothing of code should always be able to drop a saved scene in the level, press play and have everything working correctly. Designers are happy, developers have less bugs, everyone is happy 😀.
For example, lets say you're setting up a HP bar. Having the HP bar reference the player and update its contents on the process(delta)
method would work, but you would be disrespecting the golden rule I just made up, that takes you straight to game architecture jail.
A proper implementation uses Game Event and Variable references. Both are quite similar and serve the same purpose: allow different objects to trigger behavior when something happens, without having to know who triggered that event.
Game Events are a Resource
, and therefore don't exist in a scene. They can be referenced by multiple objects across scenes. Any Node
can either connect to or trigger a Game Event it references. You can create one as follows:
A node that triggers an event can do so my simply calling the trigger
method:
func on_area_entered():
my_game_event.trigger()
Any other nodes can now listen to this same even by doing:
func _enter_tree():
my_game_event.on_even.connect(thing_happened)
func _exit_tree():
# We cleanup the connection if the node is destroyed
my_game_event.on_even.disconnect(thing_happened)
func thing_happened():
print("Oh something happened!")
Variable reference take the idea of Game Events a bit further. They work the same as Game Events, except the trigger
method is called automatically when a variable changes. Lets take the player health/HP bar example, in this case the player changes its health by doing:
func on_damage_taken(damage : float):
hp_reference.value -= damage
The HP bar node can just connect to that event by doing:
func _enter_tree():
hp_reference.on_value_changed.connect(value_updated)
func _exit_tree():
# We cleanup the connection if the node is destroyed
hp_reference.on_value_changed.disconnect(value_updated)
func value_updated(old_value : float, new_value : float):
update_hp_bar(new_value)
Note that the HP bar doesn't need to know about the player, it only references a FloatReference
and can therefore be added to a level and work without extra setup. The player also doesn't need to know about the HP bar.
You can easily add these for your own custom types by extending either GameEvent
and VariableReference
. See CameraAreaEvent and FloatReference for examples on how to do this.