ET框架6.0分析一、ECS架构
概述
ET框架的ECS架构是从ECS原生设计思想变形而来的(关于ECS架构的分析可以参考跳转链接:《ECS架构分析》),其特点是:
- Entity:实体可以作为组件挂载到其他实体上,Entity之间可以有父子嵌套关系,和其他ECS架构一样,Entity只允许是纯数据的(除了基本接口)
- System:和其他ECS架构相比,一样的是系统是纯函数。不一样的是ET的系统不是“自驱”的,而是响应式的。与其说是System,个人倒是觉得可以认为是EventHandle事件处理函数。
ET框架是基于U3D的,它的Entity和System的有点像GameObject的数据和函数的拆分:Entity有GameObject的组件式设计、父子嵌套、序列化等,而函数则被分成了多个系统(或者说事件响应函数),即被分成了Awake、Start、Update等等多个事件。和GameObject一样,这套ECS是ET用户开发者开发业务的“基石”。
Entity详解
Entity继承了IDisposable,在删除的时候必须调用Dispose方法,以防非托管资源泄露
EntityStatus 状态标签标志位
- IsFromPool:当使用对象池管理时,被置为true,这样就会在Dispose时会被对象池回收
- IsRegister:这里的注册的语义可以被理解成是否全面被纳入ET框架管理,有:
- 加入ET的Entity树,可以被遍历更新
- 在U3D环境下,会根据自身的ViewName,在U3D创建此Entity在U3D世界的GameObject的View映射(并映射父子关系)
- 抛出Regigster消息
- IsComponent:Entity是否作为组件,Dispose时若是组件,从其Entity(在ET是写作parent,其语义就是被挂载到实体)中移除此组件,若不是就是从其父节点中移除此节点。(可以推论出,ET不允许Entity即作为子节点又作为组件)
- IsCreated:是否被创造出来的,目前看起来只是在设置Domain时可能刚反序列化出来,抛一个反序列化事件出去。
- IsNew:区分被创建还是被反序列化出来的,被创建出来被对象池管理
父子关系嵌套
- 和其他父子节点一样,子节点关联父节点的生命周期(创建、更新、销毁、序列化等)
- parent 父节点或者自身作为组件所挂在在的Entity,取决于上述的IsComponent标志位
- childern 子节点表
- childernDB 会被序列化的子节点表,在AddToChildren时会判断这个子节点是不是要被序列化,是则进表
- 创建子节点时不要自己去new,使用Entity.AddChild的一系列接口
组件
- 作为组件时,与父子关系嵌套类似,组件关联实体的生命周期(创建、更新、销毁、序列化等)
- parent 父节点或者自身作为组件所挂在在的Entity,取决于上述的IsComponent标志位(同上)
- components 组件表
- componentsDB 会被序列化的组件表,操作类似上述父子关系
- 同样,创建组件时也不应自己去New,使用AddComponent的一系列接口
InstanceId 实例Id
- 由IdGenerator产生,每个进程每秒最多产生65535个实例ID,其包含时间、进程等信息
- 由于由对象池,InstanceId可以用于判断该示例是否有效
- 上述提到InstanceId带有进程信息,可以通过InstanceId锁定对象位置发消息,在Actor消息中被用到
Scene
Scene是一个特殊的Entity,Entity是具有父子嵌套结构的,可以形成树形结构,而Scene则被定义树的根,它可以(注意是可以)没有父节点,其他普通的(例如单例可能是例外)Entity必须有父节点或者作为组件挂载在Entity上。通过Scene来维护一棵Entity树。
Domain
指向Entity所在的那棵树的根节点,是指下述层次中的ZoneScene
Zone
Scene的Id,在服务器端作为区服的索引id
层级
先来看看常见的客户端模块生命周期管理分层:
- App(Game)层:进入App时被初始化,持续整个应用程序生命周期。有资源管理模块,定时器模块,...
- User(Player)层:跟随玩家登录登出变化,登入被初始化,登出被清理掉。有背包模块,技能模块,...
- Scene层:随场景变化,切场景时初始化并清理上一个场景,并刷新某些关联模块,GC... 有地图,玩家角色单位,怪物 ...
ET的客户端和上述类似,有:
- Game + 单例:类似上述App层,有计数器、配置表、资源管理等单例组件
- ZoneScene:类似上述User层,有UI、技能、任务、背包等组件
- CurrentScene:当前地图(场景),有玩家、怪物、NPC等单位,还有场景相关的组件
在服务器上,则不太一样:
- GameScene:管理进程必备的基础组件
- ZoneScene:当前ZoneScene业务相关的组件,比如Gate类型的ZoneScene包含GateSessionKeyCompontent,而Map类型的不用。
- CurrentScene:服务器多数服务不需要,可能地图服务器或者战斗服务器会用到,像聊天服务大多数都用不到。
System详解
ET框架的ECS架构的System,其最明显的特征它是响应式的,说是System,感觉更像是平时用的EventHandle事件处理函数
事件机制EventSystem
引述官方文档的介绍:
ECS最重要的特性一是数据跟逻辑分离,二是数据驱动逻辑。什么是数据驱动逻辑呢?不太好理解,我们举个例子:
一个moba游戏,英雄都有血条,血条会在人物头上显示,也会在左上方头像UI上显示。这时候服务端发来一个扣血消息。我们怎么处理这个消息?第一种方法,在消息处理函数中修改英雄的血数值,修改头像上血条显示,同时修改头像UI的血条。这种方式很明显造成了模块间的耦合。第二种方法,扣血消息处理函数中只是改变血值,血值的改变抛出一个hpchange的事件,人物头像模块跟UI模块都订阅血值改变事件,在订阅的方法中分别处理自己的逻辑,这样各个模块负责自己的逻辑,没有耦合。
这里的事件机制被赋予了更多的意义,也就是ECS的System的核心意义:使业务更加的内聚,感知不到多个组件聚合在Entity中带来的耦合。
事件类型
关联Entity的事件类型
范式为:
public class AAABBBSystem: BBBSystem<AAA>
{
public override void BBB(AAA aaa)
{
}
}
/*
- AAA是Entity的派生类
- BBB是ET框架内置的一些事件名,如Awake、Start、Update...
- 事件的抛出可以带N个参数,通过泛型处理的, 类似上述片段变形BBBSystem<AAA, Param1>这样
例如类型为Player的Entity的Awake事件订阅处理:
*/
public class PlayerAwakeSystem: AwakeSystem<Player>
{
public override void Awake(Player self)
{
//DoSomething
}
}
ET框架自动抛出
- AwakeSystem:组件工厂创建组件后抛出,只抛出一次
- StartSystem:Entity在UpdateSystem调用前抛出
- UpdateSystem:Entity每帧抛出
- DestroySystem:Entity被删除抛出
- DeserializeSystem:Entity反序列化时抛出
- LoadSystem:EventSystem加载dll时抛出,用于服务端热更新,重新加载dll做一些处理,比如重新注册handler
开发者手动抛出
- ChangeSystem:组件内容改变时抛出若需要开发者进行抛出
开发者自定义事件
引述官方示例:
int oldhp = 10;
int newhp = 5;
// 抛出hp改变事件
Game.EventSystem.Run("HpChange", oldhp, newhp);
// UI订阅hp改变事件
[Event("HpChange")]
public class HpChange_ShowUI: AEvent<int, int>
{
public override void Run(int a, int b)
{
throw new NotImplementedException();
}
}
// 模型头顶血条模块也订阅hp改变事件
[Event("HpChange")]
public class HpChange_ModelHeadChange: AEvent<int, int>
{
public override void Run(int a, int b)
{
throw new NotImplementedException();
}
}
可以看到:
- 使用字符串作为事件的key
- 使用C#特性(Attrbute)Event对响应事件的处理类标记,且该处理类继承AEvent
- 可以带若干参数,据官方文档最多三个,有需要可以自行拓展
消息事件
引述官方示例:
除此之外还有很多事件,例如消息事件。消息事件使用MessageHandler来声明,可以带参数指定哪种服务器需要订阅,更具体的消息事件可以参考消息模块。
[MessageHandler(AppType.Gate)]
public class C2G_LoginGateHandler : AMRpcHandler<C2G_LoginGate, G2C_LoginGate>
{
protected override void Run(Session session, C2G_LoginGate message, Action<G2C_LoginGate> reply)
{
G2C_LoginGate response = new G2C_LoginGate();
reply(response);
}
}
ECS架构用例
组件的组装可以封装起来,比如工厂模式,这里只是示意
// 首先得有一个父节点
Entity parent = XXX
Human human = parent.AddChild<Human>();
Head head = human.AddComponent<Head>();
head.AddComponent<Eye>();
head.AddComponent<Mouse>();
head.AddComponent<Nose>();
head.AddComponent<Ear>();
class Eye: Entity
{
public string Color { get; set; }
}
// 订阅Eye的Awake事件处理(AddComponent时抛出的)
public class EyeAwakeSystem: AwakeSystem<Eye>
{
public override void Awake(Eye self)
{
self.Color = "Black";
}
}
// ...
ET框架6.0分析一、ECS架构的更多相关文章
- ENode框架Conference案例分析系列之 - 架构设计
Conference架构概述 先贴一下Conference案例的在线地址,UI因为完全拿了微软的实现,所以都是英文的,以后我有空再改为中文的. Conference后台会议管理:http://www. ...
- 【PHP】用了这么久的Laravel框架,你分析过核心架构了没
Laravel最初的设计是为了面向MVC架构的,它可以满足如事件处理.用户身份验证等各种需求.另外它还有一个由管理数据库强力支持,用于管理模块化和可扩展性代码的软件包管理器. Laravel以其简洁. ...
- ENode框架Conference案例分析系列之 - 文章索引
ENode框架Conference案例分析系列之 - 业务简介 ENode框架Conference案例分析系列之 - 上下文划分和领域建模 ENode框架Conference案例分析系列之 - 架构设 ...
- jQuery 2.0.3 源码分析core - 整体架构
拜读一个开源框架,最想学到的就是设计的思想和实现的技巧. 废话不多说,jquery这么多年了分析都写烂了,老早以前就拜读过, 不过这几年都是做移动端,一直御用zepto, 最近抽出点时间把jquery ...
- YII框架源码分析(百度PHP大牛创作-原版-无广告无水印)
YII 框架源码分析 百度联盟事业部——黄银锋 目 录 1. 引言 3 1.1.Yii 简介 3 1.2.本文内容与结构 3 2.组件化与模块化 4 2.1.框架加载和运行流程 4 ...
- enode框架step by step之事件驱动架构(EDA)思想的在框架中如何体现
enode框架step by step之事件驱动架构(EDA)思想的在框架中如何体现 上一篇文章,我给大家分享了我的一个基于DDD以及EDA架构的框架enode,但是只是介绍了一个大概.接下来我准备用 ...
- Spark RPC框架源码分析(一)简述
Spark RPC系列: Spark RPC框架源码分析(一)运行时序 Spark RPC框架源码分析(二)运行时序 Spark RPC框架源码分析(三)运行时序 一. Spark rpc框架概述 S ...
- Spark2.1.0模型设计与基本架构(上)
随着近十年互联网的迅猛发展,越来越多的人融入了互联网——利用搜索引擎查询词条或问题:社交圈子从现实搬到了Facebook.Twitter.微信等社交平台上:女孩子们现在少了逛街,多了在各大电商平台上的 ...
- 基于TILE-GX实现快速数据包处理框架-netlib实现分析【转】
最近在研究suricata源码,在匹配模式的时候,有tilegx mpipe mode,转载下文,了解一下. 原文地址:http://blog.csdn.net/lhl_blog/article/de ...
- jQuery源码分析系列 : 整体架构
query这么多年了分析都写烂了,老早以前就拜读过, 不过这几年都是做移动端,一直御用zepto, 最近抽出点时间把jquery又给扫一遍 我也不会照本宣科的翻译源码,结合自己的实际经验一起拜读吧! ...
随机推荐
- java代码审计-SpEL表达式注入
0x01 前言 Spring Expression Language(简称 SpEL)是一种功能强大的表达式语言.用于在运行时查询和操作对象图:语法上类似于Unified EL,但提供了更多的特性,特 ...
- java的数据和表达式
一.基本语法元素 1.空白和注释及语句 (1)空白: 换行符.回车符.空格键.水平定位键(Tab) 编译器会忽略掉多余的空白 作用:增加程序的易读性 (2)注释:主要作用是将代码解释其功能和作用,在编 ...
- 利用MVC三层架构做一个案例(利用MyBatis实现增删改查)
查询所有 利用昨天学习到的MyBatis知识,再加上servlet页面的跳转, demo1.jsp UserMapper.java(接口) servletDemo.java MyBatisDemo.j ...
- Java数据脱敏(手机号|邮箱号|身份证号|银行卡号)
参考博客:https://blog.csdn.net/ywb201314/article/details/107762279
- Activiti7开发(二)-流程定义
目录 1.部署流程模型为流程定义 2.挂起/激活流程定义 3.删除流程定义 4.查询流程定义 5.上传并部署流程定义 6.查看流程模型 1.部署流程模型为流程定义 @PostMapping(value ...
- Mysql 局域网远程连接设置——Windows
工作中,遇到mysql数据库存储于我的电脑上,而其他电脑需要共同进行读写数据(类似redis并发),所以我的电脑就必须开启mysql远程连接. 一. 授权 1. 连接数据库 mysql -uroot ...
- 如何申请 Azure OpenAI
一.前言 众所周知 OpenAI ChatGPT 是不对中国开放的,包括香港.就最近一个月的情况来看,陆续有 API 调用被限制.大规模账号封禁.关闭注册.无法直接使用银联支付(国内信用卡)等等,使用 ...
- C++中va_list, va_start, va_arg, va_end的基本用法
关于va_list, va_start, va_arg, va_end 由于在C语言中没有函数重载,解决不定数目函数参数问题变得比较麻烦,即使采用C++,如果参数个数不能确定,也很难采用函数重载.对这 ...
- 打造自己的ChatGPT:逐字打印的流式处理
接口的延迟 在调用OpenAI的接口时,不免会有很慢的感觉,抛去地理位置上的网络延迟,大量的延迟往往发生在响应生成的过程中. 因此,如果使用同步接口的话,需要等待响应完全生成之后才能最终显示输出结果, ...
- 派生,super 多态与多态性 组合
派生的方法与重用: 方法一:指名道姓的调用某一类函数 >>> class Teacher(People): ... def __init__(self,name,sex,age,ti ...