Add FloatingTextManager and FloatingText for displaying UI messages; update CollectableComponent and HealthComponent to utilize new floating text features

This commit is contained in:
2025-09-11 04:35:30 +02:00
parent f9cb59d182
commit f229ff5b7d
16 changed files with 131 additions and 139 deletions

View File

@@ -1,56 +0,0 @@
using System.Collections.Generic;
using Godot;
using Mr.BrickAdventures.scripts.components;
using Mr.BrickAdventures.scripts.UI;
namespace Mr.BrickAdventures.Autoloads;
public partial class DamageNumberManager : Node
{
[Export] public PackedScene DamageNumberScene { get; set; }
private readonly List<Node> _managedNodes = [];
public void Register(Node node)
{
if (_managedNodes.Contains(node)) return;
var healthComponent = node.GetNodeOrNull<HealthComponent>("HealthComponent");
if (healthComponent == null)
{
GD.PrintErr($"Node '{node.Name}' tried to register with DamageNumberManager but has no HealthComponent.");
return;
}
healthComponent.HealthChanged += (delta, total) => OnHealthChanged(healthComponent, delta);
node.TreeExiting += () => Unregister(node);
_managedNodes.Add(node);
}
public void Unregister(Node node)
{
var healthComponent = node.GetNodeOrNull<HealthComponent>("HealthComponent");
if (healthComponent != null)
{
healthComponent.HealthChanged -= (delta, _) => OnHealthChanged(healthComponent, delta);
}
_managedNodes.Remove(node);
}
private void OnHealthChanged(HealthComponent healthComponent, float delta)
{
if (delta >= 0) return;
if (DamageNumberScene == null)
{
GD.PrintErr("DamageNumberManager: DamageNumberScene is not set!");
return;
}
var damageNumber = DamageNumberScene.Instantiate<DamageNumber>();
GetTree().CurrentScene.AddChild(damageNumber);
var position = healthComponent.GlobalPosition;
damageNumber.ShowDamage(Mathf.Abs(delta), position);
}
}

View File

@@ -1 +0,0 @@
uid://i06td076ej68

View File

@@ -0,0 +1,52 @@
using System.Globalization;
using Godot;
using Mr.BrickAdventures.scripts.UI;
namespace Mr.BrickAdventures.Autoloads;
public partial class FloatingTextManager : Node
{
[Export] public PackedScene FloatingTextScene { get; set; }
[ExportGroup("Colors")]
[Export] public Color DamageColor { get; set; } = new Color("#ff4b4b"); // Red
[Export] public Color HealColor { get; set; } = new Color("#4bff65"); // Green
[Export] public Color CoinColor { get; set; } = new Color("#ffd700"); // Gold
[Export] public Color MessageColor { get; set; } = new Color("#ffffff"); // White
public void ShowDamage(float amount, Vector2 position)
{
var text = Mathf.Round(amount * 100f).ToString(CultureInfo.InvariantCulture);
CreateFloatingText(text, position, DamageColor);
}
public void ShowHeal(float amount, Vector2 position)
{
var text = $"+{Mathf.Round(amount)}";
CreateFloatingText(text, position, HealColor);
}
public void ShowCoin(int amount, Vector2 position)
{
var text = $"+{amount}";
CreateFloatingText(text, position, CoinColor);
}
public void ShowMessage(string message, Vector2 position)
{
CreateFloatingText(message, position, MessageColor);
}
private void CreateFloatingText(string text, Vector2 position, Color color)
{
if (FloatingTextScene == null)
{
GD.PushError("FloatingTextManager: FloatingTextScene is not set!");
return;
}
var popup = FloatingTextScene.Instantiate<FloatingText>();
GetTree().CurrentScene.AddChild(popup);
popup.Show(text, position, color);
}
}

View File

@@ -120,7 +120,7 @@ dotnet/embed_build_outputs=false
name="Linux"
platform="Linux"
runnable=true
advanced_options=true
advanced_options=false
dedicated_server=false
custom_features=""
export_filter="all_resources"

View File

@@ -1,8 +0,0 @@
[gd_scene load_steps=3 format=3 uid="uid://dpibw6s3dcggr"]
[ext_resource type="Script" uid="uid://i06td076ej68" path="res://Autoloads/DamageNumberManager.cs" id="1_me007"]
[ext_resource type="PackedScene" uid="uid://c7mp0o2goauyy" path="res://objects/ui/damage_number.tscn" id="2_ghslv"]
[node name="DamageNumberManager" type="Node"]
script = ExtResource("1_me007")
DamageNumberScene = ExtResource("2_ghslv")

View File

@@ -1,4 +1,4 @@
[gd_scene load_steps=53 format=3 uid="uid://bqi5s710xb1ju"]
[gd_scene load_steps=52 format=3 uid="uid://bqi5s710xb1ju"]
[ext_resource type="Script" uid="uid://csel4s0e4g5uf" path="res://scripts/components/PlayerController.cs" id="1_yysbb"]
[ext_resource type="Shader" uid="uid://bs4xvm4qkurpr" path="res://shaders/hit_flash.tres" id="2_lgb3u"]
@@ -37,7 +37,6 @@
[ext_resource type="Script" uid="uid://dr3uv0j7n75s" path="res://scripts/components/ShipShooterComponent.cs" id="30_usc1p"]
[ext_resource type="AudioStream" uid="uid://dyev46uqusimi" path="res://sfx/shoot.wav" id="32_x2b7c"]
[ext_resource type="PackedScene" uid="uid://dtem8jgcyoqar" path="res://objects/entities/green_laser.tscn" id="36_oxudy"]
[ext_resource type="Script" uid="uid://diw6opv6yutgi" path="res://scripts/components/KillPlayerOutOfScreenComponent.cs" id="37_qec3q"]
[ext_resource type="Script" uid="uid://dupnaark1f7gm" path="res://scripts/components/ProgressiveDamageComponent.cs" id="38_dhjci"]
[ext_resource type="Script" uid="uid://dssa2taiwktis" path="res://scripts/components/Movement/PlayerInputHandler.cs" id="42_e5pae"]
[ext_resource type="Script" uid="uid://ceoxet1nqws8w" path="res://scripts/components/SpriteTilterComponent.cs" id="43_xuhvf"]
@@ -278,11 +277,6 @@ gizmo_extents = 1.0
position = Vector2(0, 3)
scale = Vector2(0.8, 1.9)
[node name="KillPlayerOutOfScreen" type="Node" parent="." node_paths=PackedStringArray("ScreenNotifier", "HealthComponent")]
script = ExtResource("37_qec3q")
ScreenNotifier = NodePath("../VisibleOnScreenNotifier2D")
HealthComponent = NodePath("../HealthComponent")
[node name="PlayerInputHandler" type="Node" parent="."]
script = ExtResource("42_e5pae")
metadata/_custom_type_script = "uid://dssa2taiwktis"

View File

@@ -1,15 +0,0 @@
[gd_scene load_steps=2 format=3 uid="uid://c7mp0o2goauyy"]
[ext_resource type="Script" uid="uid://bbupymh6krrgx" path="res://scripts/UI/DamageNumber.cs" id="1_yv42p"]
[node name="DamageNumber" type="Label"]
offset_right = 40.0
offset_bottom = 8.0
theme_override_constants/outline_size = 1
theme_override_constants/shadow_outline_size = 3
theme_override_font_sizes/font_size = 8
text = "23"
horizontal_alignment = 1
vertical_alignment = 1
script = ExtResource("1_yv42p")
metadata/_custom_type_script = "uid://bbupymh6krrgx"

View File

@@ -40,8 +40,8 @@ LimboConsole="*res://addons/limbo_console/limbo_console.gd"
DialogueManager="*res://addons/dialogue_manager/dialogue_manager.gd"
SteamManager="*res://Autoloads/SteamManager.cs"
AchievementManager="*res://objects/achievement_manager.tscn"
DamageNumberManager="*res://objects/damage_number_manager.tscn"
SkillManager="*res://objects/skill_manager.tscn"
FloatingTextManager="*res://objects/floating_text_manager.tscn"
[debug]

View File

@@ -1,4 +1,4 @@
[gd_scene load_steps=22 format=4 uid="uid://bol7g83v2accs"]
[gd_scene load_steps=23 format=4 uid="uid://bol7g83v2accs"]
[ext_resource type="PackedScene" uid="uid://bqi5s710xb1ju" path="res://objects/entities/brick_player.tscn" id="1_dnj2y"]
[ext_resource type="PackedScene" uid="uid://cawlpch2lk3a2" path="res://objects/level/world_environment.tscn" id="2_1vw1j"]
@@ -13,6 +13,7 @@
[ext_resource type="TileSet" uid="uid://bc5a20s6kuy8e" path="res://resources/tilesets/village/entities.tres" id="10_ei558"]
[ext_resource type="TileSet" uid="uid://bbppo0irxdmqy" path="res://resources/tilesets/village/foreground.tres" id="11_w7c5i"]
[ext_resource type="PackedScene" uid="uid://d0s2abysa86rq" path="res://objects/entities/child.tscn" id="15_vqevu"]
[ext_resource type="PackedScene" uid="uid://bqom4cm7r18db" path="res://objects/entities/killzone.tscn" id="16_bxal3"]
[ext_resource type="PackedScene" uid="uid://12jnkdygpxwc" path="res://objects/entities/exit_level.tscn" id="16_chnw1"]
[ext_resource type="PackedScene" uid="uid://b4pdt1gv2ymyi" path="res://objects/tooltip.tscn" id="18_4bhfj"]
@@ -57,10 +58,7 @@ ease = 2
[node name="Brick Player" parent="." instance=ExtResource("1_dnj2y")]
z_index = 10
[node name="SkillManager" parent="Brick Player" index="15"]
script = null
[node name="HitParticles" parent="Brick Player" index="26"]
[node name="HitParticles" parent="Brick Player" index="25"]
process_material = SubResource("ParticleProcessMaterial_lgb3u")
[node name="WorldEnvironment" parent="." instance=ExtResource("2_1vw1j")]
@@ -144,6 +142,9 @@ Text = "LEVEL_1_TOOLTIP_2"
position = Vector2(-59, -247)
Text = "LEVEL_1_TOOLTIP_3"
[node name="Killzone" parent="." instance=ExtResource("16_bxal3")]
position = Vector2(704, 337)
[connection signal="Death" from="Brick Player/HealthComponent" to="UI Layer/DeathScreen" method="OnPlayerDeath"]
[connection signal="Death" from="Brick Player/HealthComponent" to="UI Layer/GameOverScreen" method="OnPlayerDeath"]

View File

@@ -1,4 +1,4 @@
[gd_scene load_steps=25 format=4 uid="uid://chqb11pfoqmeb"]
[gd_scene load_steps=26 format=4 uid="uid://chqb11pfoqmeb"]
[ext_resource type="PackedScene" uid="uid://bqi5s710xb1ju" path="res://objects/entities/brick_player.tscn" id="1_wcma7"]
[ext_resource type="PackedScene" uid="uid://cawlpch2lk3a2" path="res://objects/level/world_environment.tscn" id="2_ot3dy"]
@@ -13,6 +13,7 @@
[ext_resource type="TileSet" uid="uid://bbppo0irxdmqy" path="res://resources/tilesets/village/foreground.tres" id="11_r0ngp"]
[ext_resource type="TileSet" uid="uid://ccffmjebvuoaj" path="res://resources/tilesets/village/small_foreground.tres" id="13_lc0ll"]
[ext_resource type="PackedScene" uid="uid://12jnkdygpxwc" path="res://objects/entities/exit_level.tscn" id="15_hcsb6"]
[ext_resource type="PackedScene" uid="uid://bockkmyn8il4c" path="res://objects/entities/basic_enemy.tscn" id="16_h4uvs"]
[ext_resource type="PackedScene" uid="uid://d0s2abysa86rq" path="res://objects/entities/child.tscn" id="16_lc0ll"]
[ext_resource type="PackedScene" uid="uid://to2xnqev0pu1" path="res://objects/entities/cage.tscn" id="17_h4uvs"]
[ext_resource type="PackedScene" uid="uid://bwdlmualj6xbw" path="res://objects/entities/enemy.tscn" id="18_girek"]
@@ -59,6 +60,7 @@ ease = 2
[node name="Brick Player" parent="." instance=ExtResource("1_wcma7")]
z_index = 1
position = Vector2(-203, 9)
[node name="HitParticles" parent="Brick Player" index="25"]
process_material = SubResource("ParticleProcessMaterial_lgb3u")
@@ -66,9 +68,6 @@ process_material = SubResource("ParticleProcessMaterial_lgb3u")
[node name="VisibleOnScreenNotifier2D" parent="Brick Player" index="28"]
process_mode = 4
[node name="KillPlayerOutOfScreen" parent="Brick Player" index="29"]
process_mode = 4
[node name="WorldEnvironment" parent="." instance=ExtResource("2_ot3dy")]
[node name="UI Layer" parent="." instance=ExtResource("3_cjqhe")]
@@ -84,7 +83,7 @@ ComponentsToDisable = [NodePath("../../Brick Player")]
[node name="Camera2D" parent="." instance=ExtResource("5_8nvkd")]
physics_interpolation_mode = 1
position = Vector2(32, -16)
position = Vector2(-171, -7)
process_callback = 0
limit_left = -1952
limit_top = -1744
@@ -93,7 +92,7 @@ limit_bottom = 560
[node name="PhantomCamera2D" type="Node2D" parent="." node_paths=PackedStringArray("follow_target")]
top_level = true
position = Vector2(32, -16)
position = Vector2(-171, -7)
script = ExtResource("6_ono4h")
follow_mode = 5
follow_target = NodePath("../Brick Player")
@@ -142,6 +141,18 @@ position = Vector2(-1329, 184)
[node name="Enemies" type="Node" parent="."]
[node name="Enemy5" parent="Enemies" instance=ExtResource("16_h4uvs")]
position = Vector2(187, -784)
[node name="Enemy7" parent="Enemies" instance=ExtResource("16_h4uvs")]
position = Vector2(792, -784)
[node name="Enemy8" parent="Enemies" instance=ExtResource("16_h4uvs")]
position = Vector2(1147, -783)
[node name="Enemy10" parent="Enemies" instance=ExtResource("16_h4uvs")]
position = Vector2(1272, -143)
[node name="Enemy" parent="Enemies" instance=ExtResource("18_girek")]
position = Vector2(-1526, 256)
@@ -154,21 +165,9 @@ position = Vector2(-1190, 175)
[node name="Enemy4" parent="Enemies" instance=ExtResource("18_girek")]
position = Vector2(-1326, 320)
[node name="Enemy5" parent="Enemies" instance=ExtResource("18_girek")]
position = Vector2(1272, -143)
[node name="Enemy6" parent="Enemies" instance=ExtResource("18_girek")]
position = Vector2(1648, -352)
[node name="Enemy7" parent="Enemies" instance=ExtResource("18_girek")]
position = Vector2(1147, -783)
[node name="Enemy8" parent="Enemies" instance=ExtResource("18_girek")]
position = Vector2(187, -784)
[node name="Enemy9" parent="Enemies" instance=ExtResource("18_girek")]
position = Vector2(792, -784)
[node name="Lever" parent="." instance=ExtResource("20_h4uvs")]
position = Vector2(-231, -776)

View File

@@ -1,4 +1,4 @@
[gd_scene load_steps=30 format=4 uid="uid://h60obxmju6mo"]
[gd_scene load_steps=31 format=4 uid="uid://h60obxmju6mo"]
[ext_resource type="PackedScene" uid="uid://dyp4i4ru2j2jh" path="res://objects/fxs/explosion_fx.tscn" id="1_p30ax"]
[ext_resource type="PackedScene" uid="uid://dx80ivlvuuew4" path="res://objects/fxs/fire_fx.tscn" id="2_a7yjf"]
@@ -19,6 +19,7 @@
[ext_resource type="PackedScene" uid="uid://b4pdt1gv2ymyi" path="res://objects/tooltip.tscn" id="18_l3a7y"]
[ext_resource type="PackedScene" uid="uid://cawlpch2lk3a2" path="res://objects/level/world_environment.tscn" id="20_embdf"]
[ext_resource type="PackedScene" uid="uid://cywsu7yrtjdog" path="res://objects/level/global_light.tscn" id="21_fytod"]
[ext_resource type="PackedScene" uid="uid://bqom4cm7r18db" path="res://objects/entities/killzone.tscn" id="21_p30ax"]
[ext_resource type="PackedScene" uid="uid://t6h2ra7kjyq" path="res://objects/entities/small_heal_potion.tscn" id="23_m6h4x"]
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_j7bvy"]
@@ -294,7 +295,7 @@ z_index = 5
position = Vector2(903, -118)
metadata/_edit_group_ = true
[node name="HitParticles" parent="Brick Player" index="26"]
[node name="HitParticles" parent="Brick Player" index="25"]
process_material = SubResource("ParticleProcessMaterial_lgb3u")
[node name="Camera2D" parent="." instance=ExtResource("12_qhkyq")]
@@ -350,6 +351,9 @@ position = Vector2(1244, -41)
[node name="SmallHealPotion3" parent="." instance=ExtResource("23_m6h4x")]
position = Vector2(1359, -42)
[node name="Killzone" parent="." instance=ExtResource("21_p30ax")]
position = Vector2(2456, 815)
[connection signal="Death" from="Brick Player/HealthComponent" to="UI Layer/DeathScreen" method="OnPlayerDeath"]
[connection signal="Death" from="Brick Player/HealthComponent" to="UI Layer/GameOverScreen" method="OnPlayerDeath"]

View File

@@ -1,4 +1,4 @@
[gd_scene load_steps=37 format=4 uid="uid://bhad760x3vvco"]
[gd_scene load_steps=38 format=4 uid="uid://bhad760x3vvco"]
[ext_resource type="PackedScene" uid="uid://bqi5s710xb1ju" path="res://objects/entities/brick_player.tscn" id="1_k3uyd"]
[ext_resource type="PackedScene" uid="uid://bon6raeddf3tu" path="res://objects/entities/chaser.tscn" id="1_xraal"]
@@ -20,6 +20,7 @@
[ext_resource type="Script" uid="uid://ctg4awndvgfd8" path="res://scripts/components/WaveTriggerComponent.cs" id="20_e6jir"]
[ext_resource type="PackedScene" uid="uid://to2xnqev0pu1" path="res://objects/entities/cage.tscn" id="20_l8w2k"]
[ext_resource type="PackedScene" uid="uid://dkqa3q6j2gof4" path="res://objects/entities/spaceship_exit.tscn" id="21_e6jir"]
[ext_resource type="Script" uid="uid://diw6opv6yutgi" path="res://scripts/components/KillPlayerOutOfScreenComponent.cs" id="21_o1yb3"]
[sub_resource type="Gradient" id="Gradient_l8w2k"]
offsets = PackedFloat32Array(0)
@@ -194,10 +195,7 @@ process_mode = 4
[node name="OneWayPlatformAbility" parent="Brick Player/Movements" index="3"]
process_mode = 4
[node name="SkillManager" parent="Brick Player" index="15"]
script = null
[node name="HitParticles" parent="Brick Player" index="26"]
[node name="HitParticles" parent="Brick Player" index="25"]
process_material = SubResource("ParticleProcessMaterial_lgb3u")
[node name="Enemies" type="Node2D" parent="."]
@@ -302,6 +300,12 @@ collision_mask = 4
[node name="CollisionShape2D" type="CollisionShape2D" parent="First Wave Area"]
shape = SubResource("RectangleShape2D_o1yb3")
[node name="KillPlayerOutOfScreenComponent" type="Node" parent="." node_paths=PackedStringArray("ScreenNotifier", "HealthComponent")]
script = ExtResource("21_o1yb3")
ScreenNotifier = NodePath("../Brick Player/VisibleOnScreenNotifier2D")
HealthComponent = NodePath("../Brick Player/HealthComponent")
metadata/_custom_type_script = "uid://diw6opv6yutgi"
[connection signal="Death" from="Brick Player/HealthComponent" to="UI Layer/DeathScreen" method="OnPlayerDeath"]
[connection signal="Death" from="Brick Player/HealthComponent" to="UI Layer/GameOverScreen" method="OnPlayerDeath"]
[connection signal="SpaceshipEntered" from="Spaceship Enter" to="Chaser/ChaseLevelComponent" method="OnShipEntered"]

View File

@@ -1 +0,0 @@
uid://bbupymh6krrgx

View File

@@ -1,41 +1,35 @@
using System.Globalization;
using Godot;
namespace Mr.BrickAdventures.scripts.UI;
[GlobalClass]
public partial class DamageNumber : Label
public partial class FloatingText : Label
{
[Export] public float Duration { get; set; } = 0.8f;
[Export] public float Duration { get; set; } = 1f;
[Export] public float FallDistance { get; set; } = 40f;
[Export] public float HorizontalDrift { get; set; } = 15f;
public void ShowDamage(float damageAmount, Vector2 position)
public void Show(string textToShow, Vector2 position, Color color)
{
Text = Mathf.Round(damageAmount * 100f).ToString(CultureInfo.InvariantCulture);
Text = textToShow;
GlobalPosition = position;
Modulate = color;
var rng = new RandomNumberGenerator();
var horizontalOffset = rng.RandfRange(-HorizontalDrift, HorizontalDrift);
var startPosition = GlobalPosition;
var endPosition = GlobalPosition + new Vector2(horizontalOffset, FallDistance);
var startColor = Colors.White;
startColor.A = 1f;
Modulate = startColor;
var tween = CreateTween();
tween.SetParallel();
var tween = CreateTween().SetParallel();
tween.TweenProperty(this, "global_position", endPosition, Duration)
.SetTrans(Tween.TransitionType.Quad)
.SetEase(Tween.EaseType.In);
.SetEase(Tween.EaseType.Out);
tween.Chain().TweenProperty(this, "modulate:a", 0f, Duration * 0.5f)
.SetTrans(Tween.TransitionType.Sine)
.SetEase(Tween.EaseType.Out);
.SetEase(Tween.EaseType.In);
tween.TweenCallback(Callable.From(QueueFree));
}
}

View File

@@ -1,5 +1,6 @@
using System;
using Godot;
using Mr.BrickAdventures.Autoloads;
using Mr.BrickAdventures.scripts.Resources;
namespace Mr.BrickAdventures.scripts.components;
@@ -15,6 +16,8 @@ public partial class CollectableComponent : Node
[Export] public AudioStreamPlayer2D Sfx {get; set; }
[Signal] public delegate void CollectedEventHandler(float amount, CollectableType type, Node2D body);
private FloatingTextManager _floatingTextManager;
public override void _Ready()
{
@@ -25,6 +28,8 @@ public partial class CollectableComponent : Node
if (Owner.HasNode("FadeAwayComponent"))
_hasFadeAway = true;
_floatingTextManager = GetNode<FloatingTextManager>("/root/FloatingTextManager");
}
private async void OnArea2DBodyEntered(Node2D body)
@@ -32,6 +37,22 @@ public partial class CollectableComponent : Node
try
{
if (!body.HasNode("CanPickUpComponent")) return;
if (Owner is Node2D ownerNode)
{
switch (Data.Type)
{
case CollectableType.Coin:
_floatingTextManager?.ShowCoin((int)Data.Amount, ownerNode.GlobalPosition);
break;
case CollectableType.Health:
_floatingTextManager?.ShowMessage("Healed!", ownerNode.GlobalPosition);
break;
case CollectableType.Kid:
_floatingTextManager?.ShowMessage("Rescued!", ownerNode.GlobalPosition);
break;
}
}
EmitSignalCollected(Data.Amount, Data.Type, body);
CollisionShape?.CallDeferred("set_disabled", true);

View File

@@ -15,12 +15,11 @@ public partial class HealthComponent : Node2D
[Signal] public delegate void HealthChangedEventHandler(float delta, float totalHealth);
[Signal] public delegate void DeathEventHandler();
private DamageNumberManager _damageNumberManager;
private FloatingTextManager _floatingTextManager;
public override void _Ready()
{
_damageNumberManager = GetNode<DamageNumberManager>("/root/DamageNumberManager");
_damageNumberManager?.Register(Owner);
_floatingTextManager = GetNode<FloatingTextManager>("/root/FloatingTextManager");
}
public void SetHealth(float newValue)
@@ -47,6 +46,11 @@ public partial class HealthComponent : Node2D
if (delta == 0.0f)
return;
if (delta < 0.0f)
_floatingTextManager?.ShowDamage(Mathf.Abs(delta), GlobalPosition);
else
_floatingTextManager?.ShowHeal(delta, GlobalPosition);
if (playSfx)
{