diff --git a/DungeonShooting_Godot/prefab/bullet/explode/Explode0001.tscn b/DungeonShooting_Godot/prefab/bullet/explode/Explode0001.tscn
index c25913b..8980f34 100644
--- a/DungeonShooting_Godot/prefab/bullet/explode/Explode0001.tscn
+++ b/DungeonShooting_Godot/prefab/bullet/explode/Explode0001.tscn
@@ -1,4 +1,4 @@
-[gd_scene load_steps=25 format=3 uid="uid://cw3c600m8rv6t"]
+[gd_scene load_steps=26 format=3 uid="uid://cw3c600m8rv6t"]
[ext_resource type="Texture2D" uid="uid://h7hkgbwj1li" path="res://resource/sprite/effects/common/Smoke.png" id="1_ctx3v"]
[ext_resource type="Script" path="res://src/game/activity/bullet/explode/Explode.cs" id="1_qn5pu"]
@@ -94,13 +94,17 @@
color_ramp = SubResource("GradientTexture1D_ftewy")
anim_offset_max = 1.0
+[sub_resource type="CircleShape2D" id="CircleShape2D_isein"]
+resource_local_to_scene = true
+
[sub_resource type="Animation" id="Animation_j6a2s"]
resource_name = "play"
+length = 4.0
step = 0.05
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
-tracks/0/path = NodePath("Sprite2D:visible")
+tracks/0/path = NodePath("Circle:visible")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
@@ -112,7 +116,7 @@
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
-tracks/1/path = NodePath("Sprite2D:frame")
+tracks/1/path = NodePath("Circle:frame")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
@@ -124,7 +128,7 @@
tracks/2/type = "value"
tracks/2/imported = false
tracks/2/enabled = true
-tracks/2/path = NodePath("Sprite2D2:visible")
+tracks/2/path = NodePath("Line:visible")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/keys = {
@@ -136,7 +140,7 @@
tracks/3/type = "value"
tracks/3/imported = false
tracks/3/enabled = true
-tracks/3/path = NodePath("Sprite2D2:frame")
+tracks/3/path = NodePath("Line:frame")
tracks/3/interp = 1
tracks/3/loop_wrap = true
tracks/3/keys = {
@@ -148,7 +152,7 @@
tracks/4/type = "value"
tracks/4/imported = false
tracks/4/enabled = true
-tracks/4/path = NodePath("GPUParticles2D:emitting")
+tracks/4/path = NodePath("ParticlesSmoke2:emitting")
tracks/4/interp = 1
tracks/4/loop_wrap = true
tracks/4/keys = {
@@ -160,7 +164,7 @@
tracks/5/type = "value"
tracks/5/imported = false
tracks/5/enabled = true
-tracks/5/path = NodePath("GPUParticles2D2:emitting")
+tracks/5/path = NodePath("ParticlesFire:emitting")
tracks/5/interp = 1
tracks/5/loop_wrap = true
tracks/5/keys = {
@@ -172,7 +176,7 @@
tracks/6/type = "value"
tracks/6/imported = false
tracks/6/enabled = true
-tracks/6/path = NodePath("GPUParticles2D3:emitting")
+tracks/6/path = NodePath("ParticlesSmoke:emitting")
tracks/6/interp = 1
tracks/6/loop_wrap = true
tracks/6/keys = {
@@ -181,13 +185,25 @@
"update": 1,
"values": [true]
}
+tracks/7/type = "value"
+tracks/7/imported = false
+tracks/7/enabled = true
+tracks/7/path = NodePath("CollisionShape2D:disabled")
+tracks/7/interp = 1
+tracks/7/loop_wrap = true
+tracks/7/keys = {
+"times": PackedFloat32Array(0, 0.2),
+"transitions": PackedFloat32Array(1, 1),
+"update": 1,
+"values": [false, true]
+}
[sub_resource type="Animation" id="Animation_20asn"]
length = 0.001
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
-tracks/0/path = NodePath("Sprite2D:visible")
+tracks/0/path = NodePath("Circle:visible")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
@@ -199,7 +215,7 @@
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
-tracks/1/path = NodePath("Sprite2D:frame")
+tracks/1/path = NodePath("Circle:frame")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
@@ -211,7 +227,7 @@
tracks/2/type = "value"
tracks/2/imported = false
tracks/2/enabled = true
-tracks/2/path = NodePath("Sprite2D2:visible")
+tracks/2/path = NodePath("Line:visible")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/keys = {
@@ -223,7 +239,7 @@
tracks/3/type = "value"
tracks/3/imported = false
tracks/3/enabled = true
-tracks/3/path = NodePath("Sprite2D2:frame")
+tracks/3/path = NodePath("Line:frame")
tracks/3/interp = 1
tracks/3/loop_wrap = true
tracks/3/keys = {
@@ -235,7 +251,7 @@
tracks/4/type = "value"
tracks/4/imported = false
tracks/4/enabled = true
-tracks/4/path = NodePath("GPUParticles2D:emitting")
+tracks/4/path = NodePath("ParticlesSmoke2:emitting")
tracks/4/interp = 1
tracks/4/loop_wrap = true
tracks/4/keys = {
@@ -247,7 +263,7 @@
tracks/5/type = "value"
tracks/5/imported = false
tracks/5/enabled = true
-tracks/5/path = NodePath("GPUParticles2D2:emitting")
+tracks/5/path = NodePath("ParticlesFire:emitting")
tracks/5/interp = 1
tracks/5/loop_wrap = true
tracks/5/keys = {
@@ -259,7 +275,7 @@
tracks/6/type = "value"
tracks/6/imported = false
tracks/6/enabled = true
-tracks/6/path = NodePath("GPUParticles2D3:emitting")
+tracks/6/path = NodePath("ParticlesSmoke:emitting")
tracks/6/interp = 1
tracks/6/loop_wrap = true
tracks/6/keys = {
@@ -268,6 +284,18 @@
"update": 1,
"values": [false]
}
+tracks/7/type = "value"
+tracks/7/imported = false
+tracks/7/enabled = true
+tracks/7/path = NodePath("CollisionShape2D:disabled")
+tracks/7/interp = 1
+tracks/7/loop_wrap = true
+tracks/7/keys = {
+"times": PackedFloat32Array(0),
+"transitions": PackedFloat32Array(1),
+"update": 1,
+"values": [false]
+}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_p6l6x"]
_data = {
@@ -276,10 +304,13 @@
}
[node name="Explode0001" type="Area2D"]
-modulate = Color(1.4, 1.4, 1.4, 1)
+modulate = Color(1.3, 1.3, 1.3, 1)
+z_index = 1
+collision_layer = 0
+monitorable = false
script = ExtResource("1_qn5pu")
-[node name="GPUParticles2D3" type="GPUParticles2D" parent="."]
+[node name="ParticlesSmoke" type="GPUParticles2D" parent="."]
material = SubResource("CanvasItemMaterial_sk5lv")
emitting = false
process_material = SubResource("ParticleProcessMaterial_8sxfm")
@@ -288,7 +319,7 @@
explosiveness = 0.6
randomness = 1.0
-[node name="GPUParticles2D" type="GPUParticles2D" parent="."]
+[node name="ParticlesSmoke2" type="GPUParticles2D" parent="."]
material = SubResource("CanvasItemMaterial_sk5lv")
emitting = false
amount = 10
@@ -298,7 +329,7 @@
explosiveness = 0.6
randomness = 1.0
-[node name="GPUParticles2D2" type="GPUParticles2D" parent="."]
+[node name="ParticlesFire" type="GPUParticles2D" parent="."]
material = SubResource("CanvasItemMaterial_sk5lv")
emitting = false
amount = 25
@@ -308,14 +339,17 @@
explosiveness = 0.7
randomness = 1.0
-[node name="Sprite2D" type="Sprite2D" parent="."]
+[node name="Circle" type="Sprite2D" parent="."]
texture = ExtResource("2_bpdnr")
hframes = 7
-[node name="Sprite2D2" type="Sprite2D" parent="."]
+[node name="Line" type="Sprite2D" parent="."]
texture = ExtResource("4_i3ry2")
hframes = 6
+[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
+shape = SubResource("CircleShape2D_isein")
+
[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
libraries = {
"": SubResource("AnimationLibrary_p6l6x")
diff --git a/DungeonShooting_Godot/scene/World.tscn b/DungeonShooting_Godot/scene/World.tscn
index 6abd441..76eca4e 100644
--- a/DungeonShooting_Godot/scene/World.tscn
+++ b/DungeonShooting_Godot/scene/World.tscn
@@ -1,7 +1,6 @@
-[gd_scene load_steps=4 format=3 uid="uid://bqf2vks5ggnsp"]
+[gd_scene load_steps=3 format=3 uid="uid://bqf2vks5ggnsp"]
[ext_resource type="Script" path="res://src/game/room/World.cs" id="1_kt3mm"]
-[ext_resource type="PackedScene" uid="uid://cw3c600m8rv6t" path="res://prefab/bullet/explode/Explode0001.tscn" id="2_pofgj"]
[sub_resource type="Environment" id="Environment_g06jj"]
background_mode = 3
@@ -64,6 +63,3 @@
[node name="AffiliationAreaRoot" type="Node2D" parent="."]
[node name="FogMaskRoot" type="Node2D" parent="."]
-
-[node name="Explode0001" parent="." instance=ExtResource("2_pofgj")]
-position = Vector2(56, 43)
diff --git a/DungeonShooting_Godot/src/framework/common/NodeExtend.cs b/DungeonShooting_Godot/src/framework/common/NodeExtend.cs
index deee5c1..e9988f0 100644
--- a/DungeonShooting_Godot/src/framework/common/NodeExtend.cs
+++ b/DungeonShooting_Godot/src/framework/common/NodeExtend.cs
@@ -50,6 +50,16 @@
{
GameApplication.Instance.World.GetRoomLayer(layer).AddChild(node);
}
+
+ ///
+ /// 将节点插入的房间物体根节点,延时调用
+ ///
+ /// 实例
+ /// 放入的层
+ public static void AddToActivityRootDeferred(this Node2D node, RoomLayerEnum layer)
+ {
+ GameApplication.Instance.World.GetRoomLayer(layer).CallDeferred(Node.MethodName.AddChild, node);
+ }
///
/// 设置Ui布局方式是否横向扩展, 如果为 true, 则 GridContainer 的宽度会撑满父物体
diff --git a/DungeonShooting_Godot/src/framework/pool/IPoolItem.cs b/DungeonShooting_Godot/src/framework/pool/IPoolItem.cs
new file mode 100644
index 0000000..6cb897a
--- /dev/null
+++ b/DungeonShooting_Godot/src/framework/pool/IPoolItem.cs
@@ -0,0 +1,23 @@
+
+///
+/// 可被对象池池回收的实例对象接口
+///
+public interface IPoolItem : IDestroy
+{
+ ///
+ /// 是否已经回收
+ ///
+ bool IsRecycled { get; set; }
+ ///
+ /// 对象唯一标识,用于在对象池中区分对象类型,可以是资源路径,也可以是配置表id
+ ///
+ string Logotype { get; set; }
+ ///
+ /// 当物体被回收时调用,也就是进入对象池
+ ///
+ void OnReclaim();
+ ///
+ ///
+ ///
+ void OnLeavePool();
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/framework/pool/ObjectPool.cs b/DungeonShooting_Godot/src/framework/pool/ObjectPool.cs
new file mode 100644
index 0000000..27f8e4c
--- /dev/null
+++ b/DungeonShooting_Godot/src/framework/pool/ObjectPool.cs
@@ -0,0 +1,71 @@
+
+using System.Collections.Generic;
+
+///
+/// 对象池,用于获取和回收常用对象,避免每次都创建一个新的
+///
+public static class ObjectPool
+{
+ private static Dictionary> _pool = new Dictionary>();
+
+ ///
+ /// 回收一个对象
+ ///
+ public static void Reclaim(IPoolItem poolItem)
+ {
+ var logotype = poolItem.Logotype;
+ if (!_pool.TryGetValue(logotype, out var poolItems))
+ {
+ poolItems = new Stack();
+ _pool.Add(logotype, poolItems);
+ }
+
+ poolItems.Push(poolItem);
+ poolItem.IsRecycled = true;
+ poolItem.OnReclaim();
+ }
+
+ ///
+ /// 根据标识从池中取出一个实例,如果没有该标识类型的实例,则返回null
+ ///
+ public static IPoolItem GetItem(string logotype)
+ {
+ if (_pool.TryGetValue(logotype, out var poolItems))
+ {
+ if (poolItems.Count > 0)
+ {
+ var poolItem = poolItems.Pop();
+ poolItem.IsRecycled = false;
+ poolItem.OnLeavePool();
+ return poolItem;
+ }
+ }
+
+ return null;
+ }
+
+ ///
+ /// 根据标识从池中取出一个实例,如果没有该标识类型的实例,则返回null
+ ///
+ public static T GetItem(string logotype) where T : IPoolItem
+ {
+ return (T)GetItem(logotype);
+ }
+
+ ///
+ /// 销毁所有池中的物体
+ ///
+ public static void DisposeAllItem()
+ {
+ foreach (var keyValuePair in _pool)
+ {
+ var poolItems = keyValuePair.Value;
+ while (poolItems.Count > 0)
+ {
+ var item = poolItems.Pop();
+ item.Destroy();
+ }
+ }
+ _pool.Clear();
+ }
+}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/GameApplication.cs b/DungeonShooting_Godot/src/game/GameApplication.cs
index 0b57564..b5a7da5 100644
--- a/DungeonShooting_Godot/src/game/GameApplication.cs
+++ b/DungeonShooting_Godot/src/game/GameApplication.cs
@@ -168,6 +168,10 @@
///
public void DestroyWorld()
{
+ //销毁池中所有物体
+ ObjectPool.DisposeAllItem();
+
+ //销毁所有物体
if (World != null)
{
ClearWorld();
diff --git a/DungeonShooting_Godot/src/game/activity/bullet/explode/Explode.cs b/DungeonShooting_Godot/src/game/activity/bullet/explode/Explode.cs
index c0fba47..b9c98bf 100644
--- a/DungeonShooting_Godot/src/game/activity/bullet/explode/Explode.cs
+++ b/DungeonShooting_Godot/src/game/activity/bullet/explode/Explode.cs
@@ -4,9 +4,41 @@
///
/// 爆炸
///
-public partial class Explode : Area2D, IDestroy
+public partial class Explode : Area2D, IPoolItem
{
+ public bool IsRecycled { get; set; }
+ public string Logotype { get; set; }
+
public bool IsDestroyed { get; private set; }
+
+ ///
+ /// 动画播放器
+ ///
+ public AnimationPlayer AnimationPlayer { get; private set; }
+ ///
+ /// 碰撞器
+ ///
+ public CollisionShape2D CollisionShape { get; private set; }
+ ///
+ /// 碰撞器形状对象
+ ///
+ public CircleShape2D CircleShape { get; private set; }
+
+ ///
+ /// 爆炸攻击的层级
+ ///
+ public uint AttackLayer { get; private set; }
+ ///
+ /// 最小伤害
+ ///
+ public int MinHarm { get; private set; }
+ ///
+ /// 最大伤害
+ ///
+ public int MaxHarm { get; private set; }
+
+ private bool _init = false;
+
public void Destroy()
{
if (IsDestroyed)
@@ -15,16 +47,60 @@
}
IsDestroyed = true;
+ QueueFree();
+ }
+
+ public void Init(uint attackLayer, float radius, int minHarm, int maxHarm)
+ {
+ if (!_init)
+ {
+ _init = true;
+ AnimationPlayer = GetNode("AnimationPlayer");
+ CollisionShape = GetNode("CollisionShape2D");
+ CircleShape = (CircleShape2D)CollisionShape.Shape;
+ AnimationPlayer.AnimationFinished += OnAnimationFinish;
+ BodyEntered += OnBodyEntered;
+ }
+
+ AttackLayer = attackLayer;
+ MinHarm = minHarm;
+ MaxHarm = maxHarm;
+ CollisionMask = attackLayer;
+ CircleShape.Radius = radius;
}
- public override void _Ready()
+ public void RunPlay()
{
GameCamera.Main.CreateShake(new Vector2(6, 6), 0.7f, true);
- GetNode("AnimationPlayer").Play(AnimatorNames.Play);
+ AnimationPlayer.Play(AnimatorNames.Play);
+ }
+
+ public void OnReclaim()
+ {
+ GetParent().RemoveChild(this);
+ }
+
+ public void OnLeavePool()
+ {
- this.CallDelayInNode(2, () =>
+ }
+
+ private void OnAnimationFinish(StringName name)
+ {
+ if (name == AnimatorNames.Play)
{
- Destroy();
- });
+ ObjectPool.Reclaim(this);
+ }
+ }
+
+ private void OnBodyEntered(Node2D node)
+ {
+ var role = node.AsActivityObject();
+ if (role != null)
+ {
+ var angle = (role.Position - Position).Angle();
+ role.CallDeferred(nameof(role.Hurt), Utils.Random.RandomRangeInt(MinHarm, MaxHarm), angle);
+ role.MoveController.AddForce(Vector2.FromAngle(angle) * 150, 300);
+ }
}
}
\ No newline at end of file
diff --git a/DungeonShooting_Godot/src/game/activity/bullet/normal/Bullet.cs b/DungeonShooting_Godot/src/game/activity/bullet/normal/Bullet.cs
index 0527b0c..fae59af 100644
--- a/DungeonShooting_Godot/src/game/activity/bullet/normal/Bullet.cs
+++ b/DungeonShooting_Godot/src/game/activity/bullet/normal/Bullet.cs
@@ -133,10 +133,12 @@
//击中爆炸,测试用
if (TriggerRole == null || !TriggerRole.IsAi)
{
- var loadAndInstantiate = ResourceManager.LoadAndInstantiate(ResourcePath.prefab_bullet_explode_Explode0001_tscn);
- loadAndInstantiate.Position = Position;
- loadAndInstantiate.RotationDegrees = Utils.Random.RandomRangeInt(0, 360);
- GetParent().AddChild(loadAndInstantiate);
+ var explode = ObjectManager.GetExplode(ResourcePath.prefab_bullet_explode_Explode0001_tscn);
+ explode.Position = Position;
+ explode.RotationDegrees = Utils.Random.RandomRangeInt(0, 360);
+ explode.AddToActivityRoot(RoomLayerEnum.YSortLayer);
+ explode.Init(AttackLayer, 25, MinHarm, MaxHarm);
+ explode.RunPlay();
}
Destroy();
@@ -185,10 +187,12 @@
//击中爆炸,测试用
if (TriggerRole == null || !TriggerRole.IsAi)
{
- var loadAndInstantiate = ResourceManager.LoadAndInstantiate(ResourcePath.prefab_bullet_explode_Explode0001_tscn);
- loadAndInstantiate.Position = Position;
- loadAndInstantiate.RotationDegrees = Utils.Random.RandomRangeInt(0, 360);
- GetParent().AddChild(loadAndInstantiate);
+ var explode = ObjectManager.GetExplode(ResourcePath.prefab_bullet_explode_Explode0001_tscn);
+ explode.Position = Position;
+ explode.RotationDegrees = Utils.Random.RandomRangeInt(0, 360);
+ explode.AddToActivityRootDeferred(RoomLayerEnum.YSortLayer);
+ explode.Init(AttackLayer, 25, MinHarm, MaxHarm);
+ explode.RunPlay();
}
Destroy();
diff --git a/DungeonShooting_Godot/src/game/activity/role/Role.cs b/DungeonShooting_Godot/src/game/activity/role/Role.cs
index fcbf036..0342a63 100644
--- a/DungeonShooting_Godot/src/game/activity/role/Role.cs
+++ b/DungeonShooting_Godot/src/game/activity/role/Role.cs
@@ -983,7 +983,7 @@
/// 受到伤害, 如果是在碰撞信号处理函数中调用该函数, 请使用 CallDeferred 来延时调用, 否则很有可能导致报错
///
/// 伤害的量
- /// 角度
+ /// 伤害角度(弧度制)
public virtual void Hurt(int damage, float angle)
{
//受伤闪烁, 无敌状态
diff --git a/DungeonShooting_Godot/src/game/manager/ObjectManager.cs b/DungeonShooting_Godot/src/game/manager/ObjectManager.cs
new file mode 100644
index 0000000..5338453
--- /dev/null
+++ b/DungeonShooting_Godot/src/game/manager/ObjectManager.cs
@@ -0,0 +1,15 @@
+
+public static class ObjectManager
+{
+ public static Explode GetExplode(string resPath)
+ {
+ var explode = ObjectPool.GetItem(resPath);
+ if (explode == null)
+ {
+ explode = ResourceManager.LoadAndInstantiate(resPath);
+ explode.Logotype = resPath;
+ }
+
+ return explode;
+ }
+}
\ No newline at end of file