From aa73e54b3e98dd9ff5aae4e413c5a5886491a55a Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Thu, 11 Sep 2025 05:09:24 +0200 Subject: [PATCH] Add KnockbackComponent and HazardComponent for enhanced enemy interactions; integrate knockback effects in DamageComponent --- objects/entities/basic_enemy.tscn | 7 ++- objects/entities/brick_player.tscn | 8 +-- objects/entities/cactus.tscn | 41 +++++++++++++++ objects/entities/enemy.tscn | 7 ++- objects/level/base_level.tscn | 8 ++- scenes/level_village_2.tscn | 4 +- scripts/components/DamageComponent.cs | 8 +++ scripts/components/HazardComponent.cs | 31 ++++++++++++ scripts/components/HazardComponent.cs.uid | 1 + scripts/components/KnockbackComponent.cs | 62 +++++++++-------------- 10 files changed, 127 insertions(+), 50 deletions(-) create mode 100644 objects/entities/cactus.tscn create mode 100644 scripts/components/HazardComponent.cs create mode 100644 scripts/components/HazardComponent.cs.uid diff --git a/objects/entities/basic_enemy.tscn b/objects/entities/basic_enemy.tscn index 7e4dc71..7f04119 100644 --- a/objects/entities/basic_enemy.tscn +++ b/objects/entities/basic_enemy.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=25 format=3 uid="uid://bockkmyn8il4c"] +[gd_scene load_steps=26 format=3 uid="uid://bockkmyn8il4c"] [ext_resource type="Shader" uid="uid://bs4xvm4qkurpr" path="res://shaders/hit_flash.tres" id="1_hh6y0"] [ext_resource type="Texture2D" uid="uid://duxx56wxmjoxd" path="res://sprites/basic_enemy.png" id="2_laaj3"] @@ -14,6 +14,7 @@ [ext_resource type="AudioStream" uid="uid://b3tsqhr06pbrs" path="res://sfx/enemy_hurt.wav" id="14_ndloc"] [ext_resource type="AudioStream" uid="uid://dyev46uqusimi" path="res://sfx/shoot.wav" id="15_cg63s"] [ext_resource type="PackedScene" uid="uid://dx80ivlvuuew4" path="res://objects/fxs/fire_fx.tscn" id="16_jqaq6"] +[ext_resource type="Script" uid="uid://cgfynrn68lp12" path="res://scripts/components/KnockbackComponent.cs" id="17_ih01d"] [ext_resource type="PackedScene" uid="uid://ck6nml06tm6ue" path="res://objects/fxs/ice_fx.tscn" id="17_o7qsb"] [ext_resource type="PackedScene" uid="uid://b12tppjkkqpt4" path="res://objects/fxs/hit_particles.tscn" id="18_v861c"] @@ -172,3 +173,7 @@ visible = false [node name="HitParticles" parent="." instance=ExtResource("18_v861c")] position = Vector2(0, 1) process_material = SubResource("ParticleProcessMaterial_pxaaa") + +[node name="KnockbackComponent" type="Node" parent="."] +script = ExtResource("17_ih01d") +metadata/_custom_type_script = "uid://cgfynrn68lp12" diff --git a/objects/entities/brick_player.tscn b/objects/entities/brick_player.tscn index 2985f95..763270d 100644 --- a/objects/entities/brick_player.tscn +++ b/objects/entities/brick_player.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=52 format=3 uid="uid://bqi5s710xb1ju"] +[gd_scene load_steps=51 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"] @@ -21,7 +21,6 @@ [ext_resource type="Script" uid="uid://ccqb8kd5m0eh7" path="res://scripts/components/ScoreComponent.cs" id="11_o1ihh"] [ext_resource type="Script" uid="uid://dgb8bqcri7nsj" path="res://scripts/components/HealthComponent.cs" id="12_ur2y5"] [ext_resource type="Script" uid="uid://byw1legrv1ep2" path="res://scripts/components/PlayerDeathComponent.cs" id="13_7til7"] -[ext_resource type="Script" uid="uid://cgfynrn68lp12" path="res://scripts/components/KnockbackComponent.cs" id="14_e5pae"] [ext_resource type="Script" uid="uid://cecelixl41t3j" path="res://scripts/components/InvulnerabilityComponent.cs" id="15_xuhvf"] [ext_resource type="Script" uid="uid://dvyd26ricriql" path="res://scripts/components/FlashingComponent.cs" id="16_uno3u"] [ext_resource type="Script" uid="uid://dtg6115je7b5s" path="res://scripts/components/StompDamageComponent.cs" id="17_bl1gx"] @@ -182,11 +181,6 @@ script = ExtResource("13_7til7") DeathSfx = NodePath("../sfx_hurt") HealthComponent = NodePath("../HealthComponent") -[node name="KnockbackComponent" type="Node" parent="." node_paths=PackedStringArray("HealthComponent")] -script = ExtResource("14_e5pae") -KnockbackForce = 1250.0 -HealthComponent = NodePath("../HealthComponent") - [node name="InvulnerabilityComponent" type="Node" parent="." node_paths=PackedStringArray("FlashingComponent")] script = ExtResource("15_xuhvf") FlashingComponent = NodePath("../FlashingComponent Base") diff --git a/objects/entities/cactus.tscn b/objects/entities/cactus.tscn new file mode 100644 index 0000000..f6358ff --- /dev/null +++ b/objects/entities/cactus.tscn @@ -0,0 +1,41 @@ +[gd_scene load_steps=6 format=3 uid="uid://un55bdc2cg2q"] + +[ext_resource type="Texture2D" uid="uid://djifxc5x0dyrw" path="res://sprites/ppc_tileset.png" id="1_1nndd"] +[ext_resource type="Script" uid="uid://cgfynrn68lp12" path="res://scripts/components/KnockbackComponent.cs" id="2_bljtt"] +[ext_resource type="Script" uid="uid://nhu2xd8611fk" path="res://scripts/components/HazardComponent.cs" id="3_bljtt"] + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_14ml2"] +size = Vector2(14, 15) + +[sub_resource type="RectangleShape2D" id="RectangleShape2D_4q8oh"] +size = Vector2(16, 16) + +[node name="Cactus" type="StaticBody2D"] + +[node name="CollisionShape2D" type="CollisionShape2D" parent="."] +position = Vector2(0, 0.5) +shape = SubResource("RectangleShape2D_14ml2") + +[node name="Sprite2D" type="Sprite2D" parent="."] +texture = ExtResource("1_1nndd") +hframes = 12 +vframes = 12 +frame = 71 + +[node name="Area2D" type="Area2D" parent="."] +collision_mask = 12 + +[node name="CollisionShape2D" type="CollisionShape2D" parent="Area2D"] +position = Vector2(-0.5, 0) +shape = SubResource("RectangleShape2D_4q8oh") +debug_color = Color(0.285572, 0.422655, 0.118384, 0.42) + +[node name="KnockbackComponent" type="Node" parent="."] +script = ExtResource("2_bljtt") +metadata/_custom_type_script = "uid://cgfynrn68lp12" + +[node name="HazardComponent" type="Node2D" parent="." node_paths=PackedStringArray("KnockbackComponent", "HazardArea")] +script = ExtResource("3_bljtt") +KnockbackComponent = NodePath("../KnockbackComponent") +HazardArea = NodePath("../Area2D") +metadata/_custom_type_script = "uid://nhu2xd8611fk" diff --git a/objects/entities/enemy.tscn b/objects/entities/enemy.tscn index 8be4216..0d79100 100644 --- a/objects/entities/enemy.tscn +++ b/objects/entities/enemy.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=27 format=3 uid="uid://bwdlmualj6xbw"] +[gd_scene load_steps=28 format=3 uid="uid://bwdlmualj6xbw"] [ext_resource type="Shader" uid="uid://bs4xvm4qkurpr" path="res://shaders/hit_flash.tres" id="1_ep4yr"] [ext_resource type="Texture2D" uid="uid://cu72810eyk4dx" path="res://sprites/enemy-robot.png" id="2_hjtwe"] @@ -18,6 +18,7 @@ [ext_resource type="PackedScene" uid="uid://dx80ivlvuuew4" path="res://objects/fxs/fire_fx.tscn" id="15_mc6rj"] [ext_resource type="PackedScene" uid="uid://ck6nml06tm6ue" path="res://objects/fxs/ice_fx.tscn" id="16_68hnm"] [ext_resource type="PackedScene" uid="uid://b12tppjkkqpt4" path="res://objects/fxs/hit_particles.tscn" id="18_pxaaa"] +[ext_resource type="Script" uid="uid://cgfynrn68lp12" path="res://scripts/components/KnockbackComponent.cs" id="19_xku20"] [sub_resource type="RectangleShape2D" id="RectangleShape2D_pwwji"] size = Vector2(25, 31) @@ -182,3 +183,7 @@ visible = false [node name="HitParticles" parent="." instance=ExtResource("18_pxaaa")] position = Vector2(0, 1) process_material = SubResource("ParticleProcessMaterial_pxaaa") + +[node name="KnockbackComponent" type="Node" parent="."] +script = ExtResource("19_xku20") +metadata/_custom_type_script = "uid://cgfynrn68lp12" diff --git a/objects/level/base_level.tscn b/objects/level/base_level.tscn index 1761ec5..d5a07ad 100644 --- a/objects/level/base_level.tscn +++ b/objects/level/base_level.tscn @@ -53,15 +53,19 @@ ease = 2 [node name="Brick Player" parent="." instance=ExtResource("1_lbnsn")] -[node name="HitParticles" parent="Brick Player" index="26"] +[node name="HitParticles" parent="Brick Player" index="24"] process_material = SubResource("ParticleProcessMaterial_lgb3u") [node name="WorldEnvironment" parent="." instance=ExtResource("1_hb5r3")] [node name="UI Layer" parent="." instance=ExtResource("2_lbnsn")] -[node name="Marketplace" parent="UI Layer" index="3" node_paths=PackedStringArray("SkillUnlockerComponent")] +[node name="HUD" parent="UI Layer" index="0" node_paths=PackedStringArray("Health")] +Health = NodePath("../../Brick Player/HealthComponent") + +[node name="Marketplace" parent="UI Layer" index="3" node_paths=PackedStringArray("SkillUnlockerComponent", "ComponentsToDisable")] SkillUnlockerComponent = NodePath("../../Brick Player/SkillUnlockerComponent") +ComponentsToDisable = [NodePath("../../Brick Player")] [node name="Global Light" parent="." instance=ExtResource("3_3732a")] diff --git a/scenes/level_village_2.tscn b/scenes/level_village_2.tscn index cb1f5e8..0275868 100644 --- a/scenes/level_village_2.tscn +++ b/scenes/level_village_2.tscn @@ -62,10 +62,10 @@ ease = 2 z_index = 1 position = Vector2(-203, 9) -[node name="HitParticles" parent="Brick Player" index="25"] +[node name="HitParticles" parent="Brick Player" index="24"] process_material = SubResource("ParticleProcessMaterial_lgb3u") -[node name="VisibleOnScreenNotifier2D" parent="Brick Player" index="28"] +[node name="VisibleOnScreenNotifier2D" parent="Brick Player" index="27"] process_mode = 4 [node name="WorldEnvironment" parent="." instance=ExtResource("2_ot3dy")] diff --git a/scripts/components/DamageComponent.cs b/scripts/components/DamageComponent.cs index ce2a9ed..eb9992b 100644 --- a/scripts/components/DamageComponent.cs +++ b/scripts/components/DamageComponent.cs @@ -12,11 +12,14 @@ public partial class DamageComponent : Node [Export] public Timer DamageTimer { get; set; } private Node _currentTarget = null; + private KnockbackComponent _knockbackComponent = null; [Signal] public delegate void EffectInflictedEventHandler(Node2D target, StatusEffectDataResource effect); public override void _Ready() { + _knockbackComponent = Owner.GetNodeOrNull("KnockbackComponent"); + if (Area != null) { Area.BodyEntered += OnAreaBodyEntered; @@ -93,6 +96,11 @@ public partial class DamageComponent : Node DealDamage(health); + if (_knockbackComponent != null && body is CharacterBody2D characterBody && Owner is Node2D source) + { + _knockbackComponent.ApplyKnockback(characterBody, source); + } + inv?.Activate(); } diff --git a/scripts/components/HazardComponent.cs b/scripts/components/HazardComponent.cs new file mode 100644 index 0000000..3a18b4e --- /dev/null +++ b/scripts/components/HazardComponent.cs @@ -0,0 +1,31 @@ +using Godot; + +namespace Mr.BrickAdventures.scripts.components; + +[GlobalClass] +public partial class HazardComponent : Node2D +{ + [Export] public KnockbackComponent KnockbackComponent { get; set; } + [Export] public Area2D HazardArea { get; set; } + + public override void _Ready() + { + if (KnockbackComponent == null) + { + GD.PrintErr("HazardComponent requires a KnockbackComponent to function properly."); + SetProcess(false); + return; + } + + HazardArea.BodyEntered += OnBodyEntered; + } + + private void OnBodyEntered(Node2D body) + { + GD.Print($"Node {body.Name} entered hazard area."); + if (body is CharacterBody2D characterBody && Owner is Node2D source) + { + KnockbackComponent.ApplyKnockback(characterBody, source); + } + } +} \ No newline at end of file diff --git a/scripts/components/HazardComponent.cs.uid b/scripts/components/HazardComponent.cs.uid new file mode 100644 index 0000000..db75c3b --- /dev/null +++ b/scripts/components/HazardComponent.cs.uid @@ -0,0 +1 @@ +uid://nhu2xd8611fk diff --git a/scripts/components/KnockbackComponent.cs b/scripts/components/KnockbackComponent.cs index 2b506e5..a3dd2ba 100644 --- a/scripts/components/KnockbackComponent.cs +++ b/scripts/components/KnockbackComponent.cs @@ -5,45 +5,33 @@ namespace Mr.BrickAdventures.scripts.components; [GlobalClass] public partial class KnockbackComponent : Node { - [Export] public CharacterBody2D Body { get; set; } - [Export] public float KnockbackForce { get; set; } = 25f; - [Export] public HealthComponent HealthComponent { get; set; } - - private bool _knockbackMode = false; - private int _knockbackFrames = 0; - - public override void _Ready() - { - HealthComponent.HealthChanged += OnHealthChanged; - } - - public override void _Process(double delta) - { - if (_knockbackMode) _knockbackFrames++; - - if (_knockbackFrames <= 1) return; - - _knockbackMode = false; - _knockbackFrames = 0; - } - - public override void _PhysicsProcess(double delta) - { - if (_knockbackMode) ApplyKnockback((float)delta); - } + [Export] public float KnockbackForce { get; set; } = 400f; + [Export] public float KnockbackDuration { get; set; } = 0.2f; // Duration in seconds - private void OnHealthChanged(float delta, float totalHealth) + /// + /// Applies a knockback force to a target body, pushing it away from a source. + /// + /// The CharacterBody2D to apply the knockback to. + /// The Node2D causing the knockback (e.g., the enemy, the cactus). + public void ApplyKnockback(CharacterBody2D target, Node2D source) { - if (totalHealth <= 0f || delta >= 0f) return; - - _knockbackMode = true; - } + if (target == null || source == null) + { + return; + } - private void ApplyKnockback(float delta) - { - var velocity = Body.Velocity.Normalized(); - var knockbackDirection = new Vector2(Mathf.Sign(velocity.X), 0.4f); - var knockbackVector = -knockbackDirection * KnockbackForce * delta; - Body.Velocity += knockbackVector; + var direction = (target.GlobalPosition - source.GlobalPosition).Normalized(); + + if (direction == Vector2.Zero) + { + direction = Vector2.Up; + } + + target.Velocity = direction * KnockbackForce; + + var tween = CreateTween(); + tween.TweenProperty(target, "velocity", Vector2.Zero, KnockbackDuration) + .SetEase(Tween.EaseType.Out) + .SetTrans(Tween.TransitionType.Quad); } } \ No newline at end of file