TEngine框架学习 实体(Entity)模块

本文章都是建立在Winodws平台下测试的,其他平台是否同样适用还未进行验证。
TE框架还在更新中,未来可能有较大变动。

TEngine: 2025.5.20

前言

本篇文章中的实体模块都是根据GF模块自行封装进TE框架的,原版TE框架暂时还没有实体模块! 本模块非常依赖框架中的对象池(IObjectPool)应用池(PoolReference) 模块。另外,本模块可能存在一些博主还未发现的Bug,欢迎指正。

所有的功能和代码的演示都是基于以下项目演示的,欢迎大家参考。
GitHub: 基于TEngine框架实现的塔防Demo

实体模块介绍

Entity(实体)模块是框架用来管理游戏场景中动态创建的所有物体(如角色、怪物、武器、特效等)的核心模块,它把这类对象抽象为“实体”,并提供了一套生命周期管理、分组、复用和挂接机制,让开发者可以高效、灵活地处理动态对象。博主个人的理解就是“实体模块就是强化后的对象池”。

模块脚本解析

下表是实体模块的四个基类的介绍,这些基类是作为模块(Module)添加进TE框架中的(再一次体现出TE框架的优秀,自定义模块添增减的非常便利,完美解耦)。

脚本 说明
Entity 不负责任何游戏逻辑,只提供“身份、位置、生命周期”的最小公共接口。
EntityLogic 开发者编写的逻辑类,用于处理实体的生命周期(如 OnShowOnHide等)。
EntityData 每个实体都需要一个数据类,类似于一次性变量容器,用于存储实体的配置信息(如类型、位置、属性等),用于初始化时传参。
EntityModule 框架内部用于记录所有实体和实体组的组件,提供实体的生成、销毁、查询和Entity的轮询。

下表是实体模块应用层的主要脚本。

脚本 说明
EntityDataControl 为了方便对象池管理。这是实际上生成、销毁实体的类,通过该类再调用框架里的EntityModule
xxxEntity 继承自Entity,管理模块销毁时推入对象池。
xxxEntityLogic 继承自EntityLogic,实体的逻辑处理都在这里完成。
xxxEntityData 每个实体都需要一个数据类,继承自EntityLogic,类似于一次性变量容器,用于存储实体的配置信息(如类型、位置、属性等),用于初始化时传参

总结一下,实体就是GameObjecg+Entity脚本+EngityLogic脚本,初始化时由EntityData传参,有自己的对象池管理和生命周期管理。

本模块与GF实体模块的区别

博主自己封装的实体模块和GF的实体模块有很大不同,这里简单讲讲区别:

  1. 最大的区别就是移除了GF框架中的EntityGroup模块,用TE的对象池和应用池管理。

  2. GF框架中Entity脚本EntityLogic脚本都是运行时添加,更进一步还要读表——找到对应Logic脚本——添加脚本到GameObject,博主这里就不整这么麻烦了,都是手动提前挂在好的。

  3. 为每个实体添加了一个唯一的int m_SerialId标示,所有的查找、销毁、存储都会用到。

  4. 实体存在子实体的情况,这里稍微简化了,移除了GF框架中的相关功能,但是添加了一个链表List<int> childSerialIds存储子实体的序列号

实现代码

以下代码仅供参考,不是很完整,具体请参考前言中的Demo

下面会贴出实体模块的三个模块脚本(EntityModule、Entity、EntityLogic),一个应用层管理脚本(EntityDataControl), 还有一个其他脚本(EntityData)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

namespace TEngine
{
public interface IEntityModule
{
public int GenerateSerialId();

public void HideEntity(int serialId);
public void HideAllEntity();
public void HideEntity(Entity entity);

public void AddToDic(int serialId, Entity entity);
public void RemoveFromDic(int serialId);

public IEnumerable<Entity> GetAllEntities();

public void Clear();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

namespace TEngine
{
public class EntityModule : Module, IUpdateModule, IEntityModule
{
private Dictionary<int, Entity> _dicSerial2Entity;

protected int serialId;
private IEntityModule _entityModuleImplementation;

/// <summary>
/// 获取游戏框架模块优先级。
/// </summary>
/// <remarks>优先级较高的模块会优先轮询,并且关闭操作会后进行。</remarks>
public override int Priority => 7;

public override void OnInit()
{
_dicSerial2Entity = new Dictionary<int, Entity>();
serialId = 0;
}

public override void Shutdown()
{
}

public void Update(float elapseSeconds, float realElapseSeconds)
{
try
{
var entities = new List<Entity>(_dicSerial2Entity.Values);

foreach (var entity in entities)
{
if (entity != null && entity.Logic != null)
{
entity.Logic.OnUpdate(elapseSeconds, realElapseSeconds);
}
}
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}

public int GenerateSerialId()
{
serialId += 1;
return serialId;
}

public void HideEntity(Entity entity)
{
if (entity == null)
return;

Log.Debug("EntityModuleEx Hide Entity {0}", entity.SerialId);
HideEntity(entity.SerialId);
}

public void HideEntity(int serialId)
{
Entity entity = null;
if (!_dicSerial2Entity.TryGetValue(serialId, out entity))
{
Log.Error("Can find entity('serial id:{0}') ", serialId);
}

Entity tempEntity = _dicSerial2Entity[serialId];
List<int> childSerialIds = tempEntity.GetChildrenIds();
RemoveFromDic(serialId);

Log.Debug("HideEntity serialId:{0} entity count:{1} childSerialIds:{2}", serialId, _dicSerial2Entity.Count, childSerialIds.Count);

if (childSerialIds.Count > 0)
{
foreach (var item in childSerialIds)
{
if (_dicSerial2Entity.ContainsKey(item))
{
HideEntity(item);
}
}
}

tempEntity.OnHide(true, null);
tempEntity.Clear();
}

public void HideAllEntity()
{
List<int> towerSerialIds = new List<int>(_dicSerial2Entity.Keys);
for (int i = 0; i < towerSerialIds.Count; i++)
{
HideEntity(towerSerialIds[i]);
}
}

public void AddToDic(int serialId, Entity entity)
{
if (!_dicSerial2Entity.ContainsKey(serialId))
{
_dicSerial2Entity.Add(serialId, entity);
Log.Debug("AddToDic serialId:{0} entity count:{1}", serialId, _dicSerial2Entity.Count);
}
else
{
_dicSerial2Entity[serialId] = entity;
Log.Debug("UpdateToDic serialId:{0} entity count:{1}", serialId, _dicSerial2Entity.Count);
}
}

public void RemoveFromDic(int serialId)
{
if (_dicSerial2Entity.ContainsKey(serialId))
{
_dicSerial2Entity.Remove(serialId);
}
}

public IEnumerable<Entity> GetAllEntities()
{
return _dicSerial2Entity.Values;
}

public void Clear()
{
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
using System;
using System.Collections.Generic;
using TEngine;
using UnityEngine;

namespace TEngine
{
public abstract class Entity : MonoBehaviour, IMemory
{
private int m_Id;
private int m_SerialId; //序列号Id
private string m_EntityAssetName;
private EntityLogic m_EntityLogic;
private List<int> childSerialIds = new List<int>(5);

/// <summary>
/// 获取实体编号。
/// </summary>
public int Id
{
get { return m_Id; }
}

/// <summary>
/// 获取实体序列号Id。
/// </summary>
public int SerialId
{
get { return m_SerialId; }
}

/// <summary>
/// 获取实体资源名称。
/// </summary>
public string EntityAssetName
{
get { return m_EntityAssetName; }
}

/// <summary>
/// 获取实体实例。
/// </summary>
public object Handle
{
get { return gameObject; }
}

/// <summary>
/// 获取实体逻辑。
/// </summary>
public EntityLogic Logic
{
get { return m_EntityLogic; }
}

/// <summary>
/// 实体初始化。
/// </summary>
/// <param name="entityId">实体编号。</param>
/// <param name="entityAssetName">实体资源名称。</param>
/// <param name="userData">用户自定义数据。</param>
public virtual void OnInit(int entityId, int serialId, string entityAssetName, EntityLogic entityLogic)
{
m_Id = entityId;
m_SerialId = serialId;
m_EntityAssetName = entityAssetName;
m_EntityLogic = entityLogic;
}

/// <summary>
/// 实体回收。
/// </summary>
public void OnRecycle()
{
try
{
m_EntityLogic.OnRecycle();
m_EntityLogic.enabled = false;
}
catch (Exception exception)
{
Log.Error("Entity '[{0}]{1}' OnRecycle with exception '{2}'.", m_Id.ToString(), m_EntityAssetName, exception.ToString());
}

m_Id = 0;
}

/// <summary>
/// 实体显示。
/// </summary>
/// <param name="userData">用户自定义数据。</param>
public void OnShow(object userData)
{
try
{
m_EntityLogic.OnShow(userData);
}
catch (Exception exception)
{
Log.Error("Entity '[{0}]{1}' OnShow with exception '{2}'.", m_Id.ToString(), m_EntityAssetName, exception.ToString());
}
}

/// <summary>
/// 实体隐藏。
/// </summary>
/// <param name="isShutdown">是否是关闭实体管理器时触发。</param>
/// <param name="userData">用户自定义数据。</param>
public virtual void OnHide(bool isShutdown, object userData)
{
try
{
Log.Debug("OnHide {0}", m_EntityAssetName);
m_EntityLogic.OnHide(isShutdown, userData);
m_Id = 0;
m_EntityAssetName = string.Empty;
childSerialIds.Clear();
}
catch (Exception exception)
{
Log.Error("Entity '[{0}]{1}' OnHide with exception '{2}'.", m_Id.ToString(), m_EntityAssetName, exception.ToString());
}
}

public void OnAttachedId(int SerialId)
{
childSerialIds.Add(SerialId);
}

public void OnDetachedId(int SerialIds)
{
for (int i = 0; i < childSerialIds.Count; i++)
{
if (childSerialIds[i] == SerialIds)
{
childSerialIds.RemoveAt(i);
break;
}
}
}

public List<int> GetChildrenIds()
{
return childSerialIds;
}

public virtual void Clear()
{
m_Id = 0;
m_EntityAssetName = string.Empty;
childSerialIds.Clear();
// PoolManager.Instance.PushGameObject(this.gameObject);
}

/// <summary>
/// 实体轮询。
/// </summary>
/// <param name="elapseSeconds">逻辑流逝时间,以秒为单位。</param>
/// <param name="realElapseSeconds">真实流逝时间,以秒为单位。</param>
public void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
try
{
m_EntityLogic.OnUpdate(elapseSeconds, realElapseSeconds);
}
catch (Exception exception)
{
Log.Error("Entity '[{0}]{1}' OnUpdate with exception '{2}'.", m_Id.ToString(), m_EntityAssetName, exception.ToString());
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
using System;
using System.Collections;
using System.Collections.Generic;
using TEngine;
using UnityEngine;

namespace TEngine
{
/// <summary>
/// 实体逻辑基类。
/// </summary>
public abstract class EntityLogic : MonoBehaviour, IMemory
{
private bool m_Available = false;
private bool m_Visible = false;
private Entity m_Entity = null;
private Transform m_CachedTransform = null;
private int m_OriginalLayer = 0;
private Transform m_OriginalTransform = null;

/// <summary>
/// 获取实体。
/// </summary>
public Entity Entity
{
get
{
return m_Entity;
}
}

/// <summary>
/// 获取或设置实体名称。
/// </summary>
public string Name
{
get
{
return gameObject.name;
}
set
{
gameObject.name = value;
}
}

/// <summary>
/// 获取实体是否可用。
/// </summary>
public bool Available
{
get
{
return m_Available;
}
}

/// <summary>
/// 获取或设置实体是否可见。
/// </summary>
public bool Visible
{
get
{
return m_Available && m_Visible;
}
set
{
if (!m_Available)
{
Log.Warning("Entity '{0}' is not available.", Name);
return;
}

if (m_Visible == value)
{
return;
}

m_Visible = value;
InternalSetVisible(value);
}
}

/// <summary>
/// 获取已缓存的 Transform。
/// </summary>
public Transform CachedTransform
{
get
{
return m_CachedTransform;
}
}

/// <summary>
/// 实体初始化。
/// </summary>
/// <param name="userData">用户自定义数据。</param>
public virtual void OnInit(object userData)
{
if (m_CachedTransform == null)
{
m_CachedTransform = transform;
}

m_Entity = GetComponent<Entity>();
m_OriginalLayer = gameObject.layer;
m_OriginalTransform = CachedTransform.parent;
}

/// <summary>
/// 实体回收。
/// </summary>
protected internal virtual void OnRecycle()
{
}

/// <summary>
/// 实体显示。
/// </summary>
/// <param name="userData">用户自定义数据。</param>
protected internal virtual void OnShow(object userData)
{
m_Available = true;
Visible = true;
}

/// <summary>
/// 实体隐藏。
/// </summary>
/// <param name="isShutdown">是否是关闭实体管理器时触发。</param>
/// <param name="userData">用户自定义数据。</param>
protected internal virtual void OnHide(bool isShutdown, object userData)
{
if (this == null || gameObject == null)
{
Log.Warning("EntityLogic.OnHide: Object already destroyed.");
return;
}
// TODO: 待实现方法 递归实现游戏层次
// gameObject.SetLayerRecursively(m_OriginalLayer);
Visible = false;
m_Available = false;
}

/// <summary>
/// 实体附加子实体。
/// </summary>
/// <param name="childEntity">附加的子实体。</param>
/// <param name="parentTransform">被附加父实体的位置。</param>
/// <param name="userData">用户自定义数据。</param>
protected internal virtual void OnAttached(EntityLogic childEntity, Transform parentTransform, object userData)
{
}

/// <summary>
/// 实体解除子实体。
/// </summary>
/// <param name="childEntity">解除的子实体。</param>
/// <param name="userData">用户自定义数据。</param>
protected internal virtual void OnDetached(EntityLogic childEntity, object userData)
{
}

/// <summary>
/// 实体附加子实体。
/// </summary>
/// <param name="parentEntity">被附加的父实体。</param>
/// <param name="parentTransform">被附加父实体的位置。</param>
/// <param name="userData">用户自定义数据。</param>
protected internal virtual void OnAttachTo(EntityLogic parentEntity, Transform parentTransform, object userData)
{
CachedTransform.SetParent(parentTransform);
}

/// <summary>
/// 实体解除子实体。
/// </summary>
/// <param name="parentEntity">被解除的父实体。</param>
/// <param name="userData">用户自定义数据。</param>
protected internal virtual void OnDetachFrom(EntityLogic parentEntity, object userData)
{
CachedTransform.SetParent(m_OriginalTransform);
}

/// <summary>
/// 实体轮询。
/// </summary>
/// <param name="elapseSeconds">逻辑流逝时间,以秒为单位。</param>
/// <param name="realElapseSeconds">真实流逝时间,以秒为单位。</param>
protected internal virtual void OnUpdate(float elapseSeconds, float realElapseSeconds)
{
}

/// <summary>
/// 设置实体的可见性。
/// </summary>
/// <param name="visible">实体的可见性。</param>
protected virtual void InternalSetVisible(bool visible)
{
gameObject.SetActive(visible);
}

public void Clear()
{

}
}
}

因为博主想要使用应用池,所以Demo里实体模块里没有EntityData基类脚本,实际体验上也差不多吧,有兴趣可以自己修改添加。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
using System.Collections;
using System.Collections.Generic;
using Com.LuisPedroFonseca.ProCamera2D;
using TEngine;
using UnityEngine;

namespace GameLogic
{
public class EntityData : IMemory
{
protected Vector3 m_Position = Vector3.zero;

protected Quaternion m_Rotation = Quaternion.identity;

protected Transform m_Parent = null;

protected int m_SerialId = 0;

protected ProCamera2D m_Cam = null;

public EntityData()
{
m_Position = Vector3.zero;
m_Rotation = Quaternion.identity;
UserData = null;
}

/// <summary>
/// 实体位置。
/// </summary>
public Vector3 Position
{
get { return m_Position; }
set { m_Position = value; }
}

/// <summary>
/// 实体朝向。
/// </summary>
public Quaternion Rotation
{
get { return m_Rotation; }
set { m_Rotation = value; }
}

/// <summary>
/// 实体根节点
/// </summary>
public Transform Parent
{
get { return m_Parent; }
set { m_Parent = value; }
}

public ProCamera2D Cam
{
get { return m_Cam; }
set { m_Cam = value; }
}

/// <summary>
/// 序列号码
/// </summary>
public int SerialId
{
get { return m_SerialId; }
set { m_SerialId = value; }
}

public object UserData { get; protected set; }

public static EntityData Create(int serialId, object userData = null)
{
EntityData entityData = PoolReference.Acquire<EntityData>();
entityData.Position = Vector3.zero;
entityData.Rotation = Quaternion.identity;
entityData.UserData = userData;
return entityData;
}

public static EntityData Create(Vector3 position, int serialId, object userData = null)
{
EntityData entityData = PoolReference.Acquire<EntityData>();
entityData.Position = position;
entityData.Rotation = Quaternion.identity;
entityData.UserData = userData;
entityData.m_SerialId = serialId;
return entityData;
}

public static EntityData Create(Vector3 position, int serialId, ProCamera2D camera, object userData = null)
{
EntityData entityData = PoolReference.Acquire<EntityData>();
entityData.Position = position;
entityData.Rotation = Quaternion.identity;
entityData.UserData = userData;
entityData.m_SerialId = serialId;
entityData.m_Cam = camera;
return entityData;
}


public static EntityData Create(Vector3 position, Quaternion quaternion, int serialId, object userData = null)
{
EntityData entityData = PoolReference.Acquire<EntityData>();
entityData.Position = position;
entityData.Rotation = quaternion;
entityData.UserData = userData;
entityData.m_SerialId = serialId;
return entityData;
}

public static EntityData Create(Vector3 position, int serialId, Transform parent, object userData = null)
{
EntityData entityData = PoolReference.Acquire<EntityData>();
entityData.Position = position;
entityData.UserData = userData;
entityData.Parent = parent;
entityData.m_SerialId = serialId;
return entityData;
}

public static EntityData Create(Vector3 position, Quaternion quaternion, Transform parent, int serialId, object userData = null)
{
EntityData entityData = PoolReference.Acquire<EntityData>();
entityData.Position = position;
entityData.Rotation = quaternion;
entityData.UserData = userData;
entityData.Parent = parent;
entityData.m_SerialId = serialId;
return entityData;
}

public virtual void Clear()
{
m_Position = Vector3.zero;
m_Rotation = Quaternion.identity;
UserData = null;
}
}
}

在创建和使用实体的时候,根据不同实体要获取不同的EntityLogic,博主这里偷懒就用了一个_logicMap的字典,比较呆,读者可以自行优化成利用反射获取对应的EntityLogic

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using GameConfig;
using TEngine;
using UnityEngine;
using Object = System.Object;

namespace GameLogic
{
public class EntityDataControl : Singleton<EntityDataControl>
{
private static readonly Dictionary<EnumProjectile, Type> _projectileLogicMap = new()
{
{ EnumProjectile.EntityProjectileHitscanLogic, typeof(EntityProjectileHitscanLogic) },
{ EnumProjectile.EntityProjectileBallisticLogic, typeof(EntityProjectileBallisticLogic) },
{ EnumProjectile.EntityEnergyPylonLogic, typeof(EntityEnergyPylonLogic) },
{ EnumProjectile.EntityEMPGeneratorLogic, typeof(EntityEMPGeneratorLogic) },
{ EnumProjectile.EntityProjectileWobblingHomingLogic, typeof(EntityProjectileWobblingHomingLogic) }
};

private static readonly Dictionary<Type, Action<int, int, Action<Entity>, object>> _logicMap = new()
{
{ typeof(EntityPlayerLogic), (id, serial, cb, data) => ShowEntity<EntityPlayerLogic>(id, serial, cb, data) },
{ typeof(EntityRocketPlatformLogic), (id, serial, cb, data) => ShowEntity<EntityRocketPlatformLogic>(id, serial, cb, data) },
{ typeof(EntityTowerBaseLogic), (id, serial, cb, data) => ShowEntity<EntityTowerBaseLogic>(id, serial, cb, data) },
{ typeof(EntityTowerLevelLogic), (id, serial, cb, data) => ShowEntity<EntityTowerLevelLogic>(id, serial, cb, data) },
{ typeof(EntityTowerAttackerLogic), (id, serial, cb, data) => ShowEntity<EntityTowerAttackerLogic>(id, serial, cb, data) },
{ typeof(EntityTowerPreviewLogic), (id, serial, cb, data) => ShowEntity<EntityTowerPreviewLogic>(id, serial, cb, data) },
{ typeof(EntityRadiusLogic), (id, serial, cb, data) => ShowEntity<EntityRadiusLogic>(id, serial, cb, data) },
{ typeof(EntityEnemyLogic), (id, serial, cb, data) => ShowEntity<EntityEnemyLogic>(id, serial, cb, data) },
{ typeof(EntityProjectileHitscanLogic), (id, serial, cb, data) => ShowEntity<EntityProjectileHitscanLogic>(id, serial, cb, data) },
{ typeof(EntityParticleAutoHideLogic), (id, serial, cb, data) => ShowEntity<EntityParticleAutoHideLogic>(id, serial, cb, data) },
{ typeof(EntityHPBarLogic), (id, serial, cb, data) => ShowEntity<EntityHPBarLogic>(id, serial, cb, data) },
{ typeof(EntityProjectileBallisticLogic), (id, serial, cb, data) => ShowEntity<EntityProjectileBallisticLogic>(id, serial, cb, data) },
{ typeof(EntityHideSelfProjectileLogic), (id, serial, cb, data) => ShowEntity<EntityHideSelfProjectileLogic>(id, serial, cb, data) },
{ typeof(EntityProjectileLogic), (id, serial, cb, data) => ShowEntity<EntityProjectileLogic>(id, serial, cb, data) },
{ typeof(EntityEnergyPylonLogic), (id, serial, cb, data) => ShowEntity<EntityEnergyPylonLogic>(id, serial, cb, data) },
{ typeof(EntityEMPGeneratorLogic), (id, serial, cb, data) => ShowEntity<EntityEMPGeneratorLogic>(id, serial, cb, data) },
{ typeof(EntityProjectileWobblingHomingLogic), (id, serial, cb, data) => ShowEntity<EntityProjectileWobblingHomingLogic>(id, serial, cb, data) },
{ typeof(EntityPlasmaLanceLogic), (id, serial, cb, data) => ShowEntity<EntityPlasmaLanceLogic>(id, serial, cb, data) },
{ typeof(EntityAnimationLogic), (id, serial, cb, data) => ShowEntity<EntityAnimationLogic>(id, serial, cb, data) },
};

public static Type GetProjectileLogicType(EnumProjectile projectileType)
{
if (_projectileLogicMap.TryGetValue(projectileType, out var logicType))
{
return logicType;
}

Log.Error("未找到对应的 Projectile 逻辑类型: {0}", projectileType);
return null;
}

public static void ShowEntity<TLogic>(int entityId,
int serialId,
Action<Entity> onShowSuccess,
object userData = null) where TLogic : EntityLogic
{
string pathName = AssetsDataLoader.Instance.GetItemConfig(entityId).ResourcesName;
if (pathName == null)
{
Log.Error("EntityDataControl ShowEntity pathName is null, entityId: {0}", entityId);
return;
}

GameObject gameObject = PoolManager.Instance.GetGameObject(pathName);
Entity entity = gameObject.GetComponent<Entity>();

// 泛型获取逻辑类
TLogic entityLogic = gameObject.GetComponent<TLogic>();

if (entityLogic == null)
{
throw new Exception($"GameObject {gameObject.name} 没有组件 {typeof(TLogic).Name}");
}

// 初始化Entity
entity.OnInit(entityId, serialId, pathName, entityLogic);
// 初始化EntityLogic
entityLogic.OnInit(userData);

GameModule.Entity.AddToDic(serialId, entity);
entity.OnShow(userData);

onShowSuccess?.Invoke(entity);
}


public void ShowEntity(ShowEntityEventData entityData)
{
if (!_logicMap.TryGetValue(entityData.LogicType, out var action))
{
throw new InvalidOperationException($"未知的逻辑类型: {entityData.LogicType.FullName}");
}

Log.Debug("ShowEntity entityId:{0} ", entityData.EntityId);

Action<Entity> callback = entity =>
{
entityData.OnShowSuccess?.Invoke(entity);
entityData.Clear();
};

action.Invoke(entityData.EntityId, entityData.SerialId, callback, entityData.UserData);
}


public void HideEntity(Entity entity)
{
if (entity == null)
return;

GameModule.Entity.HideEntity(entity);
}

public void HideAllEntities()
{
Log.Debug("HideAllEntities");
GameModule.Entity.HideAllEntity();
}

public IEnumerable<Entity> GetAllEntities()
{
return GameModule.Entity.GetAllEntities();
}

public void Clear()
{
}
}

public class EntityShowOptionsArgs
{
public int EntityId;
public int ManualSerialId;
public bool AutoGenerateSerialId = true;
public Type LogicType;
public Action<Entity> OnShowSuccess;
public object UserData;

public EntityShowOptionsArgs(
int entityId,
int manualSerialId,
bool autoGenerateSerialId,
Type logicType,
Action<Entity> onShowSuccess,
object userData)
{
EntityId = entityId;
ManualSerialId = manualSerialId;
AutoGenerateSerialId = autoGenerateSerialId;
LogicType = logicType;
this.OnShowSuccess = onShowSuccess;
UserData = userData;
}
}

public class ShowEntityEventData : IMemory
{
public int EntityId;
public int SerialId;
public Type LogicType;
public Object UserData;
public Action<Entity> OnShowSuccess = null;

public void Clear()
{
EntityId = 0;
SerialId = 0;
LogicType = null;
UserData = null;
OnShowSuccess = null;
}
}
}

一些示例

等级三加农炮塔
实体结构
子实体
对象池

在上面的示例中,图一是运行中的截图,加农炮台就是实体挂载实体的形式,图二和图三分别是母实体和子实体,母实体是负责调用攻击、音效、特效等业务的,子实体是负责模型、属性等业务的。
图四是对象池的运用,可以看到炮塔升到三级,但是二级一级的实体被推入对象池中等待复用,BuildPfx是构建炮塔的特效,同样也接受对象池管理。


TEngine框架学习 实体(Entity)模块
https://blog.meo39.com/2025/07/31/TEngineLean4/
作者
daydayasobi
发布于
2025年7月31日
许可协议