From e6f8989d16cdd95f9beb6bd365265a59aaedc5b7 Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Sat, 13 Sep 2025 13:53:40 +0200 Subject: [PATCH] Add EnemyControllerComponent and PeriodicShootingComponent; implement enemy shooting behavior and bullet spawning --- objects/entities/cannon.tscn | 25 ++++--- objects/entities/cannon_ray_down.tscn | 33 ---------- objects/entities/cannon_ray_left.tscn | 35 ---------- objects/entities/enemy.tscn | 18 +++-- resources/tilesets/village/entities.tres | 4 +- scenes/level_village_5.tscn | 3 +- .../components/EnemyControllerComponent.cs | 20 ++++++ .../EnemyControllerComponent.cs.uid | 1 + .../components/PeriodicShootingComponent.cs | 66 ++++++++++++------- 9 files changed, 94 insertions(+), 111 deletions(-) delete mode 100644 objects/entities/cannon_ray_down.tscn delete mode 100644 objects/entities/cannon_ray_left.tscn create mode 100644 scripts/components/EnemyControllerComponent.cs create mode 100644 scripts/components/EnemyControllerComponent.cs.uid diff --git a/objects/entities/cannon.tscn b/objects/entities/cannon.tscn index 80129e6..a400e2c 100644 --- a/objects/entities/cannon.tscn +++ b/objects/entities/cannon.tscn @@ -1,13 +1,15 @@ [gd_scene load_steps=5 format=3 uid="uid://dstko446qydsc"] [ext_resource type="Texture2D" uid="uid://djifxc5x0dyrw" path="res://sprites/ppc_tileset.png" id="1_6gptm"] -[ext_resource type="Script" uid="uid://ctfrbj52ejay4" path="res://scripts/components/DestroyableComponent.cs" id="2_q37h7"] -[ext_resource type="Script" uid="uid://dgb8bqcri7nsj" path="res://scripts/components/HealthComponent.cs" id="3_bhwy3"] +[ext_resource type="Script" uid="uid://bnaxy8cw3wrko" path="res://scripts/components/PeriodicShootingComponent.cs" id="2_q37h7"] +[ext_resource type="PackedScene" uid="uid://bhc7y4xugu4q7" path="res://objects/entities/bullet.tscn" id="3_bhwy3"] [sub_resource type="RectangleShape2D" id="RectangleShape2D_j5sus"] size = Vector2(16, 16) [node name="Cannon" type="StaticBody2D"] +collision_layer = 0 +collision_mask = 0 [node name="Sprite2D" type="Sprite2D" parent="."] texture = ExtResource("1_6gptm") @@ -15,12 +17,17 @@ hframes = 12 vframes = 12 frame = 42 -[node name="DestroyableComponent" type="Node" parent="." node_paths=PackedStringArray("Health")] -script = ExtResource("2_q37h7") -Health = NodePath("../HealthComponent") - -[node name="HealthComponent" type="Node2D" parent="."] -script = ExtResource("3_bhwy3") - [node name="CollisionShape2D" type="CollisionShape2D" parent="."] shape = SubResource("RectangleShape2D_j5sus") + +[node name="PeriodicShootingComponent" type="Node" parent="." node_paths=PackedStringArray("BulletSpawnPointRight")] +script = ExtResource("2_q37h7") +BulletScene = ExtResource("3_bhwy3") +ShootInterval = 3.5 +ShootDirection = Vector2(0, -1) +BulletSpawnPointRight = NodePath("../Bullet spawn") +ShootingIntervalVariation = 0.3 +metadata/_custom_type_script = "uid://bnaxy8cw3wrko" + +[node name="Bullet spawn" type="Marker2D" parent="."] +position = Vector2(0, -16) diff --git a/objects/entities/cannon_ray_down.tscn b/objects/entities/cannon_ray_down.tscn deleted file mode 100644 index 11e11cb..0000000 --- a/objects/entities/cannon_ray_down.tscn +++ /dev/null @@ -1,33 +0,0 @@ -[gd_scene load_steps=5 format=3 uid="uid://dfwpha0d18dmn"] - -[ext_resource type="Texture2D" uid="uid://djifxc5x0dyrw" path="res://sprites/ppc_tileset.png" id="1_rwgpm"] -[ext_resource type="Script" uid="uid://2i7p7v135u7c" path="res://scripts/components/DamageComponent.cs" id="2_hrj61"] -[ext_resource type="Script" uid="uid://df1llrbm80e02" path="res://scripts/components/BeamComponent.cs" id="3_hrj61"] - -[sub_resource type="RectangleShape2D" id="RectangleShape2D_ptfn7"] -size = Vector2(8, 16) - -[node name="Cannon Ray" type="Area2D"] -collision_layer = 0 -collision_mask = 5 - -[node name="Sprite2D" type="Sprite2D" parent="."] -texture_repeat = 2 -texture = ExtResource("1_rwgpm") -region_enabled = true -region_rect = Rect2(176, 64, 16, 16) - -[node name="CollisionShape2D" type="CollisionShape2D" parent="."] -shape = SubResource("RectangleShape2D_ptfn7") - -[node name="DamageComponent" type="Node" parent="." node_paths=PackedStringArray("Area")] -script = ExtResource("2_hrj61") -Area = NodePath("..") - -[node name="BeamComponent" type="Node2D" parent="." node_paths=PackedStringArray("Root", "Sprite", "CollisionShape")] -position = Vector2(0, -8) -script = ExtResource("3_hrj61") -ExpansionSpeed = 16.0 -Root = NodePath(".") -Sprite = NodePath("../Sprite2D") -CollisionShape = NodePath("../CollisionShape2D") diff --git a/objects/entities/cannon_ray_left.tscn b/objects/entities/cannon_ray_left.tscn deleted file mode 100644 index bd059cd..0000000 --- a/objects/entities/cannon_ray_left.tscn +++ /dev/null @@ -1,35 +0,0 @@ -[gd_scene load_steps=5 format=3 uid="uid://d3lt4rhxduv44"] - -[ext_resource type="Texture2D" uid="uid://djifxc5x0dyrw" path="res://sprites/ppc_tileset.png" id="1_l5x2w"] -[ext_resource type="Script" uid="uid://2i7p7v135u7c" path="res://scripts/components/DamageComponent.cs" id="2_0kbpg"] -[ext_resource type="Script" uid="uid://df1llrbm80e02" path="res://scripts/components/BeamComponent.cs" id="3_0kbpg"] - -[sub_resource type="RectangleShape2D" id="RectangleShape2D_ptfn7"] -size = Vector2(16, 8) - -[node name="Cannon Ray" type="Area2D"] -collision_layer = 0 -collision_mask = 5 - -[node name="Sprite2D" type="Sprite2D" parent="."] -texture_repeat = 2 -rotation = 1.5708 -texture = ExtResource("1_l5x2w") -region_enabled = true -region_rect = Rect2(176, 64, 16, 16) -region_filter_clip_enabled = true - -[node name="CollisionShape2D" type="CollisionShape2D" parent="."] -shape = SubResource("RectangleShape2D_ptfn7") - -[node name="DamageComponent" type="Node" parent="." node_paths=PackedStringArray("Area")] -script = ExtResource("2_0kbpg") -Area = NodePath("..") - -[node name="BeamComponent" type="Node2D" parent="." node_paths=PackedStringArray("Root", "Sprite", "CollisionShape")] -position = Vector2(8, 0) -script = ExtResource("3_0kbpg") -ExpansionSpeed = 16.0 -Root = NodePath("..") -Sprite = NodePath("../Sprite2D") -CollisionShape = NodePath("../CollisionShape2D") diff --git a/objects/entities/enemy.tscn b/objects/entities/enemy.tscn index 0d79100..ff421e9 100644 --- a/objects/entities/enemy.tscn +++ b/objects/entities/enemy.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=28 format=3 uid="uid://bwdlmualj6xbw"] +[gd_scene load_steps=29 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"] @@ -19,6 +19,7 @@ [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"] +[ext_resource type="Script" uid="uid://bhbgjr8ty2n85" path="res://scripts/components/EnemyControllerComponent.cs" id="20_5lji2"] [sub_resource type="RectangleShape2D" id="RectangleShape2D_pwwji"] size = Vector2(25, 31) @@ -97,13 +98,12 @@ RightRay = NodePath("../Right Ray") LeftWallRay = NodePath("../Left Wall Ray") RightWallRay = NodePath("../Right Wall Ray") -[node name="PeriodicShootingComponent" type="Node" parent="." node_paths=PackedStringArray("SideToSideMovement", "BulletSpawnRight", "BulletSpawnLeft")] +[node name="PeriodicShootingComponent" type="Node" parent="." node_paths=PackedStringArray("BulletSpawnPointRight", "BulletSpawnPointLeft")] script = ExtResource("6_lgbyy") BulletScene = ExtResource("7_r48kf") -SideToSideMovement = NodePath("../SideToSideMovement") -BulletSpawnRight = NodePath("../Sprite2D/right bullet spawn") -BulletSpawnLeft = NodePath("../Sprite2D/left bullet spawn") -ShootingIntervalVariation = 0.1 +BulletSpawnPointRight = NodePath("../Sprite2D/right bullet spawn") +BulletSpawnPointLeft = NodePath("../Sprite2D/left bullet spawn") +ShootingIntervalVariation = 0.3 [node name="EnemyDeathComponent" type="Node" parent="." node_paths=PackedStringArray("CollisionShape", "Health")] script = ExtResource("8_pxaaa") @@ -187,3 +187,9 @@ process_material = SubResource("ParticleProcessMaterial_pxaaa") [node name="KnockbackComponent" type="Node" parent="."] script = ExtResource("19_xku20") metadata/_custom_type_script = "uid://cgfynrn68lp12" + +[node name="EnemyControllerComponent" type="Node" parent="." node_paths=PackedStringArray("MovementComponent", "ShootingComponent")] +script = ExtResource("20_5lji2") +MovementComponent = NodePath("../SideToSideMovement") +ShootingComponent = NodePath("../PeriodicShootingComponent") +metadata/_custom_type_script = "uid://bhbgjr8ty2n85" diff --git a/resources/tilesets/village/entities.tres b/resources/tilesets/village/entities.tres index 13d364a..cffe7e4 100644 --- a/resources/tilesets/village/entities.tres +++ b/resources/tilesets/village/entities.tres @@ -1,4 +1,4 @@ -[gd_resource type="TileSet" load_steps=16 format=3 uid="uid://bc5a20s6kuy8e"] +[gd_resource type="TileSet" load_steps=17 format=3 uid="uid://bc5a20s6kuy8e"] [ext_resource type="Texture2D" uid="uid://djifxc5x0dyrw" path="res://sprites/ppc_tileset.png" id="1_ej5iv"] [ext_resource type="PackedScene" uid="uid://54w4wisfj8v8" path="res://objects/entities/coin.tscn" id="2_31a0q"] @@ -9,6 +9,7 @@ [ext_resource type="PackedScene" uid="uid://c0j1yun5s7kns" path="res://objects/entities/bouncing_mushroom.tscn" id="5_ov0dn"] [ext_resource type="PackedScene" uid="uid://d08dfqmirnd66" path="res://objects/entities/big_treasure.tscn" id="5_xxibl"] [ext_resource type="PackedScene" uid="uid://073ts5cxtwbl" path="res://objects/entities/treasure.tscn" id="6_fmgww"] +[ext_resource type="PackedScene" uid="uid://dstko446qydsc" path="res://objects/entities/cannon.tscn" id="6_xxibl"] [ext_resource type="PackedScene" uid="uid://cm3rixnnev1pg" path="res://objects/entities/jump_pad.tscn" id="7_0kjxj"] [ext_resource type="PackedScene" uid="uid://to2xnqev0pu1" path="res://objects/entities/cage.tscn" id="8_83o0w"] [ext_resource type="PackedScene" uid="uid://bd51frym6mm7v" path="res://objects/entities/lever.tscn" id="9_at40q"] @@ -151,6 +152,7 @@ scenes/9/scene = ExtResource("10_ivcjr") scenes/10/scene = ExtResource("3_31a0q") scenes/11/scene = ExtResource("4_x63lh") scenes/12/scene = ExtResource("5_ov0dn") +scenes/13/scene = ExtResource("6_xxibl") [resource] physics_layer_0/collision_layer = 1 diff --git a/scenes/level_village_5.tscn b/scenes/level_village_5.tscn index 5f5fcab..cf727c7 100644 --- a/scenes/level_village_5.tscn +++ b/scenes/level_village_5.tscn @@ -65,7 +65,6 @@ polygon = PackedVector2Array(-214, -1594, 571, -1596, 571, 321, -220, 317) [node name="WorldEnvironment" parent="." instance=ExtResource("2_yd68a")] [node name="UI Layer" parent="." instance=ExtResource("3_qbt57")] -visible = false [node name="HUD" parent="UI Layer" index="0" node_paths=PackedStringArray("Health")] Health = NodePath("../../Brick Player/HealthComponent") @@ -124,7 +123,7 @@ tile_map_data = PackedByteArray("AAAjABMAAQAEAAMAAAAjABIAAQAEAAIAAAAjABEAAQAEAAI tile_set = ExtResource("9_tmn5u") [node name="Entities layer" type="TileMapLayer" parent="."] -tile_map_data = PackedByteArray("AAANAAEAAQAAAAAADAA=") +tile_map_data = PackedByteArray("AAANAAEAAQAAAAAADAAWAPP/AQAAAAAADAAYAAIAAQAAAAAADQA=") tile_set = ExtResource("10_yka2u") [node name="Foreground layer" type="TileMapLayer" parent="."] diff --git a/scripts/components/EnemyControllerComponent.cs b/scripts/components/EnemyControllerComponent.cs new file mode 100644 index 0000000..305bd71 --- /dev/null +++ b/scripts/components/EnemyControllerComponent.cs @@ -0,0 +1,20 @@ +using Godot; + +namespace Mr.BrickAdventures.scripts.components; + +[GlobalClass] +public partial class EnemyControllerComponent : Node +{ + [Export] public SideToSideMovementComponent MovementComponent { get; set; } + [Export] public PeriodicShootingComponent ShootingComponent { get; set; } + + public override void _Process(double delta) + { + if (MovementComponent == null || ShootingComponent == null) return; + + if (MovementComponent.Direction != Vector2.Zero) + { + ShootingComponent.ShootDirection = MovementComponent.Direction; + } + } +} \ No newline at end of file diff --git a/scripts/components/EnemyControllerComponent.cs.uid b/scripts/components/EnemyControllerComponent.cs.uid new file mode 100644 index 0000000..eb42254 --- /dev/null +++ b/scripts/components/EnemyControllerComponent.cs.uid @@ -0,0 +1 @@ +uid://bhbgjr8ty2n85 diff --git a/scripts/components/PeriodicShootingComponent.cs b/scripts/components/PeriodicShootingComponent.cs index fa655b7..08a3cc1 100644 --- a/scripts/components/PeriodicShootingComponent.cs +++ b/scripts/components/PeriodicShootingComponent.cs @@ -1,3 +1,4 @@ +using System; using Godot; namespace Mr.BrickAdventures.scripts.components; @@ -8,31 +9,27 @@ public partial class PeriodicShootingComponent : Node [Export] public PackedScene BulletScene { get; set; } [Export] public float ShootInterval { get; set; } = 1.0f; [Export] public Vector2 ShootDirection { get; set; } = Vector2.Right; - [Export] public SideToSideMovementComponent SideToSideMovement { get; set; } - [Export] public Node2D BulletSpawnRight { get; set; } - [Export] public Node2D BulletSpawnLeft { get; set; } + [Export] public Node2D BulletSpawnPointRight { get; set; } + [Export] public Node2D BulletSpawnPointLeft { get; set; } [Export] public float ShootingIntervalVariation { get; set; } = 0.0f; private Timer _timer; + private RandomNumberGenerator _rng; public override void _Ready() { + _rng = new RandomNumberGenerator(); SetupTimer(); } - public override void _Process(double delta) - { - if (SideToSideMovement == null) return; - - ShootDirection = SideToSideMovement.Direction != Vector2.Zero ? SideToSideMovement.Direction : Vector2.Right; - } - private void SetupTimer() { - _timer = new Timer(); - _timer.WaitTime = GetShootInterval(); - _timer.OneShot = false; - _timer.Autostart = true; + _timer = new Timer + { + WaitTime = GetNextShootInterval(), + OneShot = false, + Autostart = true + }; _timer.Timeout += OnTimerTimeout; AddChild(_timer); } @@ -40,33 +37,52 @@ public partial class PeriodicShootingComponent : Node private void OnTimerTimeout() { Shoot(); - _timer.Start(); + _timer.WaitTime = GetNextShootInterval(); } - private double GetShootInterval() + private double GetNextShootInterval() { - if (ShootingIntervalVariation == 0f) return ShootInterval; + if (ShootingIntervalVariation <= 0f) + { + return ShootInterval; + } - var rng = new RandomNumberGenerator(); - return ShootInterval + rng.RandfRange(-ShootingIntervalVariation, ShootingIntervalVariation); + return Math.Max(0.01, ShootInterval + _rng.RandfRange(-ShootingIntervalVariation, ShootingIntervalVariation)); } private void Shoot() { + if (BulletScene == null) + { + GD.PushError("PeriodicShootingComponent: BulletScene is not set."); + return; + } + if (ShootDirection == Vector2.Zero) return; + + var spawnNode = (ShootDirection.X >= 0 || BulletSpawnPointLeft == null) + ? BulletSpawnPointRight + : BulletSpawnPointLeft; + + if (spawnNode == null) + { + GD.PrintErr("PeriodicShootingComponent: A suitable bullet spawn point is not set."); + return; + } - var root = Owner as Node2D; + var spawnPosition = spawnNode.GlobalPosition; + var owner = Owner as Node2D; + var ownerRotation = owner?.Rotation ?? 0f; + var bulletInstance = BulletScene.Instantiate(); - var launchComponent = bulletInstance.GetNodeOrNull("LaunchComponent"); - var spawnPosition = ShootDirection == Vector2.Right ? BulletSpawnRight.GlobalPosition : BulletSpawnLeft.GlobalPosition; - if (launchComponent != null) + if (bulletInstance.GetNodeOrNull("LaunchComponent") is { } launchComponent) { launchComponent.InitialDirection = ShootDirection; launchComponent.SpawnPosition = spawnPosition; - if (root != null) launchComponent.SpawnRotation = root.Rotation; + launchComponent.SpawnRotation = ownerRotation; } - bulletInstance.Position = spawnPosition; + bulletInstance.GlobalPosition = spawnPosition; GetTree().CurrentScene.AddChild(bulletInstance); } } \ No newline at end of file