In this part, we’re going to use the battlers’ is_selected
property we coded in the previous chapter to animate the corresponding Heads-Up Display.
Along with the character, the HUD will move forward and its label will turn brighter to help the player spot the energy cost preview.
In the picture, the difference is subtle, but with the animation, it’s easier to spot.
We’re going to design some animations first and then put them to use in code.
Open the UIBattlerHUD scene again and add an AnimationPlayer to it.
We’re going to design three animations:
Moving and animating a control node can be slightly different from a regular 2D node: you can choose to animate its Rect -> Position or its Margin properties. A Rect -> Position property track will always set both the node’s horizontal and vertical position, even if we only want to animate it on the horizontal axis.
Using the four Margin properties, we have more control. We can use them to offset the widget horizontally, even if it lives in a container.
There’s one small caveat to this method. As we list the HUDs in a VBoxContainer, if the player plays in windowed mode and manually resizes the window, the container will reset its children’s position. This is related to the UIBattlerHUDList, though, not the animations themselves.
Create a “_setup” animation that has the minimum duration, that’s set to autoplay, and add keys for:
Duplicate this animation by clicking the Animation menu, the Duplicate, and rename the copy to “select”.
I went with a duration of 0.5
seconds and the following timings.
Notice how the text’s tinting animation starts after the horizontal motion. I’ve also used the following easing curve on the margin’s leftmost keys.
It makes the animation start fast and ease out. Here’s the result.
Duplicate the “select” animation and rename it to “deselect”. I’ve made it a bit softer than the “select” one by removing some keys and changing the easings. The duration is still 0.5
seconds.
The margin’s leftmost keys have a different easing, both in and out. To get the curve below, you can click and drag towards the left, producing a negative value.
You can also right-click on the curve to open the preset menu. There, you’ll find an In-Out entry.
Using our animations is fairly straightforward. Open UIBattlerHUD.gd
and update the code like so.
onready var _anim_player: AnimationPlayer = $AnimationPlayer func setup(battler: Battler) -> void: # We already get a reference to the battler so all we have to do is connect to its # `selection_toggled` signal. battler.connect("selection_toggled", self, "_on_Battler_selection_toggled") #... # And we play an animation based on the boolean we received from the signal callback. func _on_Battler_selection_toggled(value: bool) -> void: if value: _anim_player.play("select") else: _anim_player.play("deselect")
And with that, your HUD will animate when a playable character’s turn starts, making it easier to see which is selected.
Here’s the complete UIBattlerHUD
code for reference.
# Displays a party member's name, health, and energy. class_name UIBattlerHUD extends TextureRect onready var _life_bar: TextureProgress = $UILifeBar onready var _energy_bar := $UIEnergyBar onready var _label := $Label onready var _anim_player: AnimationPlayer = $AnimationPlayer func _ready() -> void: Events.connect("combat_action_hovered", self, "_on_Events_combat_action_hovered") Events.connect("player_target_selection_done", self, "_on_Events_player_target_selection_done") # Initializes the health and energy bars using the battler's stats. func setup(battler: Battler) -> void: battler.connect("selection_toggled", self, "_on_Battler_selection_toggled") _label.text = battler.ui_data.display_name var stats: BattlerStats = battler.stats _life_bar.setup(stats.health, stats.max_health) _energy_bar.setup(stats.max_energy, stats.energy) stats.connect("health_changed", self, "_on_BattlerStats_health_changed") stats.connect("energy_changed", self, "_on_BattlerStats_energy_changed") func _on_BattlerStats_health_changed(_old_value: float, new_value: float) -> void: _life_bar.target_value = new_value func _on_BattlerStats_energy_changed(_old_value: float, new_value: float) -> void: _energy_bar.value = new_value func _on_Battler_selection_toggled(value: bool) -> void: if value: _anim_player.play("select") else: _anim_player.play("deselect") func _on_Events_combat_action_hovered(battler_name: String, energy_cost: int) -> void: if _label.text == battler_name: _energy_bar.selected_count = energy_cost func _on_Events_player_target_selection_done() -> void: _energy_bar.selected_count = 0