ET框架的基础逻辑
文章目录
ET框架的基础逻辑
ECS思想和OOP思想的区别
ECS下简易的逻辑的分发
ET框架下实体的生命周期
ET框架的Scene树
ECS思想和OOP思想的区别
ECS思想和OOP思想的区别
以传统RPG游戏为例,游戏中可能有Player,NPC,Monster等角色。传统的OOP思想主要是大量运用继承抽象多态,用来实现不同对象的需求。
而ECS则是将所有对象设看作一个实体,所有功能都看作组件,不同功能的对象其实就是挂载了不同组件的实体。所有实体都是等价的,不会出现实体间继承的状况,这样可以很轻松的完成功能多样的不同实体,并增强组件的复用性。
ECS下简易的逻辑的分发
仍用上述举例,介绍一下简单的逻辑分发过程。
Model
Model层负责定义实体和组件,在这里要定义实体Unit,以及一些组件(MoveComponent,CombatComponent等)以及还需定义UnitType的枚举,以便后续逻辑的分发处理
注意:
Model下的实体不能调用UnityAPI,与Unity交互的组件实体只能放在ModelView下
Model中只能存在数据,例如position,UnitType不能有对数据的操作
ModelView
负责定义与Unity交互的实体和组件,有一些诸如动画组件,GameObject组件用到的数据由Unity提供,就需要将这些实体组件放到这个下面。
Hotfix
Hotfix负责System行为的定义,提供创建实体和为实体挂载组件的功能。此时定义的UnitFactory工厂就应为不同的实体提供不同的创建方法,例如CreatePlayer,就应先将UnitType置为Player,为其添加MoveComponent组件等待操作。再如CreateNPC,UnitType设置为NPC后,由于npc一般不会移动则无需添加移动组件,可以添加对话组件等待。
注意:
Hotfix下只能定义行为,不能包含数据状态,只能对Model提供的数据状态进行相关操作
Hotfix下也不能使用Unity Api 对于一些需要Unity才能挂载的组件,需要放到view中执行
HotfixView
HotfixView负责需要与Unity交互的System行为,例如加载模型prefab,实例化游戏对象GameObject,若实体中需要用到GameObject对象,则还应为Unit实体添加GameObject组件,里面存有gameObject。若Unit实体还需要在场景中播放动画,还需要为其添加使用了UnityApi的AnimatorComponent。
同理,在处理与Unity交互的System行为时,不同类型的Unit也可能行为有所不同,在此需要针对UnitType提供不同的行为。
ET框架下实体的生命周期
在Hotfix下编写实体生命周期的行为,创建相应的类实现特定生命周期接口即可,xxxSystem<>,注意对应实体需实现IAwake等接口
Computer.cs
————————————————
namespace ET { /// <summary> /// 实体:主机 /// </summary> public class Computer:Entity,IAwake,IUpdate,IDestroy { } }
ComputerSystem.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ET { public class ComputerAwakeSystem:AwakeSystem<Computer> { public override void Awake(Computer self) { Log.Debug("ComputerAwake!!!!"); } public class ComputerUpdateSystem : UpdateSystem<Computer> { public override void Update(Computer self) { Log.Debug("Computer Update !!!"); } } public class ComputerDestroySystem:DestroySystem<Computer> { public override void Destroy(Computer self) { Log.Debug("Computer Destroy !!"); } } } public static class ComputerSystem { public static void Start(this Computer self) { //启动前先开启电源 self.GetComponent<PCCaseComponent>().StartPower(); Log.Debug("Computer Start!!!!"); //启动后开启显示器 self.GetComponent<MonitorsComponent>().Display(); } } }
触发实体的Awake生命周期一般是用 xxxScene.AddChild<实体名>()进行触发,表示将一个实体添加到一个场景的子节点中,即实例化过程,返回的对象即为实例化对象。 可通过实体对象.Dispose()释放掉,触发Destroy生命周期
ET框架的Scene树
在ET框架下,Scene即为场景作为根节点,根节点下可以存放多个实体或组件。但Scenen本质也是实体,所以Scene之间也会有层次关系。
游戏客户端的Scene层次结构
————————————————
GameScene
游戏客户端全局的Scene根节点,用于提供游戏客户端全局且必要的基础功能组件(资源加载管理组件,计时器组件等)
ZoneScene
用于提供玩家全局游戏业务功能逻辑组件(例如基础UI,背包界面等)
CurrentScene
代表玩家当前所在的地图场景,一般用于挂载当前场景相关的组件,切换或释放场景时回收所有实体及组件。
游戏服务端Scene层次结构
————————————————
GameScene
类似客户端,其用来挂载全局服务端所需的基础功能必备组件
ZoneScene
可以创建多个不同功能的ZoneScene, 每个不同功能的ZoneScene下挂载其应该具有的功能组件,例如网关下的NetKcpComponent,定位服务器的LocationComponent等等,一般通过SceneType的枚举对其进行逻辑分发。
不同ZoneScene可以存在一个进程上面,也可以每个都ZoneScene运行在一个单独的进程上,不同ZoneScene进程甚至可以分布在服务器集群上,大大提高了运行效率。
Scene可以动态创建和销毁(用于制作副本等临时场景)
创建Scene的一般流程
创建一个未挂载任何组件的Scene对象 Scene scene = EntitySceneFactory.CreateScene(id, instanceId, zone, sceneType, name, parent);
switch (scene.SceneType) { case SceneType.Realm: scene.AddComponent<NetKcpComponent, IPEndPoint, int>(startSceneConfig.OuterIPPort, SessionStreamDispatcherType.SessionStreamDispatcherServerOuter); break; case SceneType.Gate: scene.AddComponent<NetKcpComponent, IPEndPoint, int>(startSceneConfig.OuterIPPort, SessionStreamDispatcherType.SessionStreamDispatcherServerOuter); scene.AddComponent<PlayerComponent>(); scene.AddComponent<GateSessionKeyComponent>(); break; case SceneType.Map: scene.AddComponent<UnitComponent>(); scene.AddComponent<AOIManagerComponent>(); break; case SceneType.Location: scene.AddComponent<LocationComponent>(); break; }
创建自定义场景的基本步骤
-
在SceneType枚举中添加自定义名称
-
在SceneFactory中添加自定义场景类型所应该处理的相关逻辑
-
在Excel配置表中添加自定义场景信息,并生成相应cs文件