From b99292f5ea8cac518499c4eb52114110f7a38139 Mon Sep 17 00:00:00 2001 From: Xanatos <10531466+XanatosX@users.noreply.github.com> Date: Thu, 2 Jul 2026 21:37:37 +0200 Subject: [PATCH] feat: add scoreboard animations --- .../effect_player/scripts/EffectPlayer.gd | 22 +++++++++++ .../effect_player/scripts/EffectPlayer.gd.uid | 1 + .../game/finish_game/scenes/FinishGame.tscn | 21 ---------- .../scripts/PlayerScoreCounter.gd | 14 ++++++- .../scenes/PlayerScoreEntry.tscn | 36 ++++++++++------- .../scripts/PlayerScoreEntry.gd | 39 +++++++++++++++++-- .../scripts/player_score_winning_icon.gd | 24 +++++++++++- .../scenes/SoundEffectManager.tscn | 4 +- .../scripts/SoundManager.gd | 3 +- 9 files changed, 120 insertions(+), 44 deletions(-) create mode 100644 entities/game/effect_player/scripts/EffectPlayer.gd create mode 100644 entities/game/effect_player/scripts/EffectPlayer.gd.uid diff --git a/entities/game/effect_player/scripts/EffectPlayer.gd b/entities/game/effect_player/scripts/EffectPlayer.gd new file mode 100644 index 00000000..41804972 --- /dev/null +++ b/entities/game/effect_player/scripts/EffectPlayer.gd @@ -0,0 +1,22 @@ +class_name EffectPlayer extends Node + +@export var audio_files: Array[AudioStream] = [] + +@export_group("Volume") +@export var volume_modifier_bd: float = 0 + +@export_group("Pitch effect") +@export var enable_pitch_effect: bool +@export var min_pitch: float = 0.8 +@export var max_pitch: float = 1.2 + +func _ready() -> void: + if audio_files.size() == 0: + push_error("Missing audio file") + queue_free() + +func play_effect() -> void: + var pitch: float = 1.0 + if enable_pitch_effect: + pitch = randf_range(min_pitch, max_pitch) + GlobalSoundManager.play_sound_effect(audio_files.pick_random(), volume_modifier_bd, pitch) \ No newline at end of file diff --git a/entities/game/effect_player/scripts/EffectPlayer.gd.uid b/entities/game/effect_player/scripts/EffectPlayer.gd.uid new file mode 100644 index 00000000..c3df1439 --- /dev/null +++ b/entities/game/effect_player/scripts/EffectPlayer.gd.uid @@ -0,0 +1 @@ +uid://bfin0rde3y7nw diff --git a/entities/game/finish_game/scenes/FinishGame.tscn b/entities/game/finish_game/scenes/FinishGame.tscn index 1bf3eba2..0cc52354 100644 --- a/entities/game/finish_game/scenes/FinishGame.tscn +++ b/entities/game/finish_game/scenes/FinishGame.tscn @@ -70,27 +70,6 @@ bbcode_enabled = true text = "GAME_STATISTIC" fit_content = true -[node name="MarginContainer2" type="MarginContainer" parent="PanelContainer/MarginContainer/VBoxContainer2/BodyContainer/PanelContainer/VBoxContainer2/Header" unique_id=1439470366] -layout_mode = 2 -theme_override_constants/margin_left = 25 -theme_override_constants/margin_right = 25 - -[node name="VBoxContainer2" type="HBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer2/BodyContainer/PanelContainer/VBoxContainer2/Header/MarginContainer2" unique_id=790566321] -layout_mode = 2 -size_flags_vertical = 0 - -[node name="PlayerNameTitle" type="RichTextLabel" parent="PanelContainer/MarginContainer/VBoxContainer2/BodyContainer/PanelContainer/VBoxContainer2/Header/MarginContainer2/VBoxContainer2" unique_id=18815397] -layout_mode = 2 -size_flags_horizontal = 3 -text = "PLAYER_TITLE" -fit_content = true - -[node name="PlayerScoreTitle" type="RichTextLabel" parent="PanelContainer/MarginContainer/VBoxContainer2/BodyContainer/PanelContainer/VBoxContainer2/Header/MarginContainer2/VBoxContainer2" unique_id=2089612080] -layout_mode = 2 -size_flags_horizontal = 3 -bbcode_enabled = true -text = "PLAYER_SCORE" - [node name="Player List" type="VBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer2/BodyContainer/PanelContainer/VBoxContainer2" unique_id=193919905] layout_mode = 2 size_flags_vertical = 3 diff --git a/entities/game/player_name_overlay/scripts/PlayerScoreCounter.gd b/entities/game/player_name_overlay/scripts/PlayerScoreCounter.gd index 30e591f1..5eae64cf 100644 --- a/entities/game/player_name_overlay/scripts/PlayerScoreCounter.gd +++ b/entities/game/player_name_overlay/scripts/PlayerScoreCounter.gd @@ -1,11 +1,14 @@ class_name PlayerScoreCounter extends PlayerScoreInformation +signal animation_done() + @export var target_font_size: int = 30 @export var default_font_size: int = 20; @export var transition_duration: float = 0.4; var current_displayed_score: int = 0 var target_score: int = 0 +var _animation_running: bool = false func _ready() -> void: super() @@ -13,16 +16,25 @@ func _ready() -> void: func set_new_score(new_score: int) -> void: target_score = new_score + if _animation_running: + await animation_done animate_score() func animate_score() -> void: if current_displayed_score == target_score: return + _animation_running = true var animation_tween: Tween = create_tween() animation_tween.step_finished.connect(func(_step: int) -> void: text = str(target_score) current_displayed_score = target_score ) animation_tween.tween_property(self, "theme_override_font_sizes/font_size", target_font_size, transition_duration) - animation_tween.tween_property(self, "theme_override_font_sizes/font_size", default_font_size, transition_duration) \ No newline at end of file + animation_tween.tween_property(self, "theme_override_font_sizes/font_size", default_font_size, transition_duration) + animation_tween.finished.connect(_animation_completed) + + +func _animation_completed() -> void: + _animation_running = false + animation_done.emit() \ No newline at end of file diff --git a/entities/game/player_score_entry/scenes/PlayerScoreEntry.tscn b/entities/game/player_score_entry/scenes/PlayerScoreEntry.tscn index 1a68597f..e78ee4c7 100644 --- a/entities/game/player_score_entry/scenes/PlayerScoreEntry.tscn +++ b/entities/game/player_score_entry/scenes/PlayerScoreEntry.tscn @@ -4,10 +4,14 @@ [ext_resource type="StyleBox" uid="uid://d1rhal5rp1608" path="res://assets/styles/panel/MetalBox.tres" id="2_5y1a8"] [ext_resource type="Resource" uid="uid://c0eqsic62hvl0" path="res://shared/resources/colors/assets/PlayerHighlightColor.tres" id="2_nkn6w"] [ext_resource type="Script" uid="uid://b3vlxulgtqk1f" path="res://entities/game/player_score_entry/scripts/player_score_player_name.gd" id="4_pscha"] +[ext_resource type="PackedScene" uid="uid://darv1wt1dlt1s" path="res://entities/game/player_overlay_score_board/scenes/PlayerOverlayScoreBoard.tscn" id="5_p3c6r"] [ext_resource type="Material" uid="uid://bcry1v2yx1t6w" path="res://entities/game/player_score_entry/materials/win_material.tres" id="5_qfumn"] [ext_resource type="Texture2D" uid="uid://dpwyddj6yhp8r" path="res://assets/icons/TrophyIcon.tres" id="5_sqauh"] -[ext_resource type="Script" uid="uid://bujvldkvi65fi" path="res://entities/game/player_score_entry/scripts/player_score_counter.gd" id="6_qfumn"] [ext_resource type="Script" uid="uid://bceoil1fal6gh" path="res://entities/game/player_score_entry/scripts/player_score_winning_icon.gd" id="7_60cck"] +[ext_resource type="Script" uid="uid://bfin0rde3y7nw" path="res://entities/game/effect_player/scripts/EffectPlayer.gd" id="9_p3c6r"] +[ext_resource type="AudioStream" uid="uid://h0louy6yrv3o" path="res://assets/audio/effect/card-turn-3.ogg" id="10_md8wg"] +[ext_resource type="AudioStream" uid="uid://dhlalkc173c3p" path="res://assets/audio/effect/card-turn-1.ogg" id="11_i1551"] +[ext_resource type="AudioStream" uid="uid://48iu01xst023" path="res://assets/audio/effect/card-turn-2.ogg" id="12_sajqw"] [node name="MarginContainer" type="MarginContainer" unique_id=558595318] anchors_preset = 10 @@ -34,8 +38,15 @@ layout_mode = 2 text = "PLAYER_NAME" script = ExtResource("4_pscha") +[node name="PlayerScoreBoard" parent="PanelContainer/HBoxContainer" unique_id=574098440 instance=ExtResource("5_p3c6r")] +layout_mode = 2 +offset_transform_enabled = true +offset_transform_position = Vector2(10, 0) +max_visualized_cards = 15 + [node name="MarginContainer" type="MarginContainer" parent="PanelContainer/HBoxContainer" unique_id=1125081868] layout_mode = 2 +size_flags_horizontal = 10 theme_override_constants/margin_left = 20 [node name="WinningIcon" type="TextureRect" parent="PanelContainer/HBoxContainer/MarginContainer" unique_id=1076927301] @@ -43,25 +54,20 @@ material = ExtResource("5_qfumn") layout_mode = 2 size_flags_horizontal = 4 size_flags_vertical = 4 +offset_transform_enabled = true +offset_transform_position = Vector2(0, -3) texture = ExtResource("5_sqauh") stretch_mode = 3 script = ExtResource("7_60cck") -[node name="PlayerScore" type="RichTextLabel" parent="PanelContainer/HBoxContainer" unique_id=408698693] -layout_mode = 2 -size_flags_horizontal = 3 -size_flags_vertical = 4 -bbcode_enabled = true -text = "PLAYER_SCORE" -fit_content = true -scroll_active = false -horizontal_alignment = 2 -script = ExtResource("6_qfumn") +[node name="EffectPlayer" type="Node" parent="." unique_id=1736066145] +script = ExtResource("9_p3c6r") +audio_files = Array[AudioStream]([ExtResource("10_md8wg"), ExtResource("11_i1551"), ExtResource("12_sajqw")]) +metadata/_custom_type_script = "uid://bfin0rde3y7nw" +[connection signal="add_new_score" from="." to="PanelContainer/HBoxContainer/PlayerScoreBoard" method="add_score"] +[connection signal="add_new_score" from="." to="EffectPlayer" method="play_effect"] [connection signal="player_changed" from="." to="PanelContainer/HBoxContainer/PlayerName" method="set_text"] [connection signal="player_did_win" from="." to="PanelContainer/HBoxContainer/PlayerName" method="player_did_win"] [connection signal="player_did_win" from="." to="PanelContainer/HBoxContainer/MarginContainer/WinningIcon" method="player_did_win"] -[connection signal="player_did_win" from="." to="PanelContainer/HBoxContainer/PlayerScore" method="is_winning_player"] -[connection signal="set_player_score" from="." to="PanelContainer/HBoxContainer/PlayerScore" method="set_counter_target"] -[connection signal="counter_done" from="PanelContainer/HBoxContainer/PlayerScore" to="." method="winner_is_selected"] -[connection signal="counter_done" from="PanelContainer/HBoxContainer/PlayerScore" to="PanelContainer/HBoxContainer/MarginContainer/WinningIcon" method="scoring_is_done"] +[connection signal="winner_selected" from="." to="PanelContainer/HBoxContainer/MarginContainer/WinningIcon" method="scoring_is_done"] diff --git a/entities/game/player_score_entry/scripts/PlayerScoreEntry.gd b/entities/game/player_score_entry/scripts/PlayerScoreEntry.gd index 39cabce6..b17ac132 100644 --- a/entities/game/player_score_entry/scripts/PlayerScoreEntry.gd +++ b/entities/game/player_score_entry/scripts/PlayerScoreEntry.gd @@ -4,14 +4,47 @@ signal set_player_score(score: int) signal player_changed(name: String) signal player_did_win(color: ColorResource) signal winner_selected() +signal add_new_score() @export var player_win_color: ColorResource +@export var first_animation_time: float = 0.3 +## The time required to show the winning cards +@export var animation_start_time: float = 1 + +## The time required for the last winning card to show +@export var animation_end_time: float = 0.1 + +var _player_data: PlayerResource +var _did_win: bool = false + +func _ready() -> void: + if _player_data != null: + _trigger_timed_score(_player_data.score) + + if _did_win: + player_did_win.emit(player_win_color) func set_player(player: PlayerResource, did_win: bool) -> void: + _player_data = player player_changed.emit(player.get_display_name()) set_player_score.emit(player.score) - if did_win: - player_did_win.emit(player_win_color) + _did_win = did_win + +func _trigger_timed_score(score: int) -> void: + if score == 0: + return + await get_tree().create_timer(first_animation_time).timeout + add_new_score.emit() + score -= 1 + + for i: int in score: + var lerp_value: float = float(i) / float(score) + var time: float = lerpf(animation_start_time, animation_end_time, lerp_value) + await get_tree().create_timer(time).timeout + + add_new_score.emit() + winner_is_selected() func winner_is_selected() -> void: - winner_selected.emit() + if _did_win: + winner_selected.emit() diff --git a/entities/game/player_score_entry/scripts/player_score_winning_icon.gd b/entities/game/player_score_entry/scripts/player_score_winning_icon.gd index 9392ca5a..87665bfb 100644 --- a/entities/game/player_score_entry/scripts/player_score_winning_icon.gd +++ b/entities/game/player_score_entry/scripts/player_score_winning_icon.gd @@ -1,5 +1,13 @@ extends TextureRect +@export var animation_time: float = 0.5 +@export_group("Pulse effect") +@export var should_pulse: bool = true +## Time required to get from min to max, also from max to min +@export var pulse_cycle_time: float = 0.8 +@export var min_pulse_size: float = 0.8 +@export var max_pulse_size: float = 1.2 + var _should_be_shown: bool = false func _ready() -> void: @@ -11,5 +19,19 @@ func player_did_win(_color: ColorResource) -> void: func scoring_is_done() -> void: if not _should_be_shown: return + offset_transform_scale = Vector2.ZERO + visible = true + var tween: Tween = create_tween() + tween.tween_property(self, "offset_transform_scale", Vector2.ONE, animation_time) + await tween.finished + _pulse_icon() - visible = true \ No newline at end of file +func _pulse_icon() -> void: + print("pulse start") + if not should_pulse: + return + var pulse_tween: Tween = create_tween() + pulse_tween.set_loops() + pulse_tween.tween_property(self, "offset_transform_scale", Vector2(max_pulse_size, max_pulse_size), pulse_cycle_time) + pulse_tween.tween_property(self, "offset_transform_scale", Vector2(min_pulse_size, min_pulse_size), pulse_cycle_time) + #tween.finished.connect(_pulse_icon) \ No newline at end of file diff --git a/shared/global_entities/sound_effect_manager/scenes/SoundEffectManager.tscn b/shared/global_entities/sound_effect_manager/scenes/SoundEffectManager.tscn index a172360c..9418aac8 100644 --- a/shared/global_entities/sound_effect_manager/scenes/SoundEffectManager.tscn +++ b/shared/global_entities/sound_effect_manager/scenes/SoundEffectManager.tscn @@ -1,7 +1,7 @@ -[gd_scene load_steps=2 format=3 uid="uid://dfqgavjicy8wm"] +[gd_scene format=3 uid="uid://dfqgavjicy8wm"] [ext_resource type="Script" uid="uid://drdnu7rtkkq0e" path="res://shared/global_entities/sound_effect_manager/scripts/SoundManager.gd" id="1_x8wxi"] -[node name="SoundEffectManager" type="Node2D"] +[node name="SoundEffectManager" type="Node2D" unique_id=2022719934] script = ExtResource("1_x8wxi") audio_bus = "sfx" diff --git a/shared/global_entities/sound_effect_manager/scripts/SoundManager.gd b/shared/global_entities/sound_effect_manager/scripts/SoundManager.gd index 5eb86ac7..c40e1142 100644 --- a/shared/global_entities/sound_effect_manager/scripts/SoundManager.gd +++ b/shared/global_entities/sound_effect_manager/scripts/SoundManager.gd @@ -27,7 +27,7 @@ func create_new_audio_stream_player() -> AudioStreamPlayer2D: players += 1 return player -func play_sound_effect(sound: AudioStream, volume_db: float = 0) -> void: +func play_sound_effect(sound: AudioStream, volume_db: float = 0, pitch: float = 1.0) -> void: var free_audio_stream: AudioStreamPlayer2D = null for child: AudioStreamPlayer2D in sound_stream_node.get_children(): if !child.playing: @@ -39,6 +39,7 @@ func play_sound_effect(sound: AudioStream, volume_db: float = 0) -> void: free_audio_stream.volume_db = volume_db free_audio_stream.stream = sound + free_audio_stream.pitch_scale = pitch free_audio_stream.play() func stop_all_sounds() -> void: