断断续续2个月完成了自己的游戏demo,却有种删除整个项目的冲突,架构太混乱,已经完全不想加东西,加一个功能就会因为发现自己之前留了太多屎,到后面不得不为之前的屎埋坑,必须做出改变。我需要学习一下其他人的代码和架构,查找了一下github几个star比较高的unity框架,发现了它——Entitas,最近知乎unity讨论比较热乎的ECS架构。
那么就玩玩吧,反正我一个学生党很闲很作,而且多学学也是好的,就当为学习unity2018将出的ECS架构做个预热,更何况对于这个开源项目,无论是文档,还是教程游戏demo都很丰富,很适合我这种不容易读懂源码的菜鸡。

环境安装

https://github.com/sschmid/Entitas-CSharp/wiki/Unity-Installation-Guide
直接按官方文档做,我下载的版本是1.4。

注意Generate 前要把Project Path内的文本改掉,改成你C#项目文件的扩展名,比如我是将Assembly-CSharp.csproj改成了ESC.csproj,也就是我Vs生成的.csproj文件的文件名

Hello World

https://github.com/sschmid/Entitas-CSharp/wiki/Unity-Tutorial-Hello-World
依旧只是按官方文档做,我这篇可不是教程(因为没人看),只是自己记录学习中的思考和趟坑,同时通过这种一边学习一边总结的方法加强学习效果

创建组件

在Sources文件下创建Components文件夹,接着在Components文件夹创建DebugMessageComponent脚本,继承组件必须要继承的 IComponent接口,给类加上[Game]特性,然后回到unity,Generated 。

根据官方api文档描述,组件类还加一些属性。不过这里没加,类里面只有一个字符串数据,其他代码都会自动生成

看一下生成的代码

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by Entitas.CodeGeneration.Plugins.ComponentEntityApiGenerator.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
public partial class GameEntity {public DebugMessageComponent debugMessage { get { return (DebugMessageComponent)GetComponent(GameComponentsLookup.DebugMessage); } }public bool hasDebugMessage { get { return HasComponent(GameComponentsLookup.DebugMessage); } }public void AddDebugMessage(string newMessage) {var index = GameComponentsLookup.DebugMessage;var component = CreateComponent<DebugMessageComponent>(index);component.message = newMessage;AddComponent(index, component);}public void ReplaceDebugMessage(string newMessage) {var index = GameComponentsLookup.DebugMessage;var component = CreateComponent<DebugMessageComponent>(index);component.message = newMessage;ReplaceComponent(index, component);}public void RemoveDebugMessage() {RemoveComponent(GameComponentsLookup.DebugMessage);}
}//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by Entitas.CodeGeneration.Plugins.ComponentMatcherApiGenerator.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
public sealed partial class GameMatcher {static Entitas.IMatcher<GameEntity> _matcherDebugMessage;public static Entitas.IMatcher<GameEntity> DebugMessage {get {if (_matcherDebugMessage == null) {var matcher = (Entitas.Matcher<GameEntity>)Entitas.Matcher<GameEntity>.AllOf(GameComponentsLookup.DebugMessage);matcher.componentNames = GameComponentsLookup.componentNames;_matcherDebugMessage = matcher;}return _matcherDebugMessage;}}
}

文件名比之前添加的组件类名多了个”Game”,文件内有2个分部类,
实际上是给GameEntity类增加了 debugMessage属性,(hasDebugMessage,AddDebugMessage,ReplaceDebugMessage,RemoveDebugMessage)四个方法,这些个方法一看名字就知道啥意思了,再看另一个密封类GameMatcher ,看不懂!!实体匹配?我瞎猜的,以后再研究。

创建反应系统

在Sources文件下创建Systems文件夹,接着在Systems文件夹创建DebugMessageSystem脚本,继承ReactiveSystem< GameEntity>这个抽象类,作为一个反应系统,用来监听变化,哇!这个比自己写事件监听机制作舒服多了

using System.Collections.Generic;
using UnityEngine;
using Entitas;
public class DebugMessageSystem : ReactiveSystem<GameEntity>
{public DebugMessageSystem(Contexts contexts) : base(contexts.game){}protected override void Execute(List<GameEntity> entities){foreach (var e in entities){Debug.Log(e.debugMessage.message);}}protected override bool Filter(GameEntity entity){return entity.hasDebugMessage;}protected override ICollector<GameEntity> GetTrigger(IContext<GameEntity> context){return context.CreateCollector(GameMatcher.DebugMessage);}
}

ReactiveSystem< GameEntity>接口需要手动实现3个方法GetTrigger,Filter,Execute。
GetTrigger是利用之前生成的GameMatcher进行筛选实体,Filter?笨木头大佬说是以后作第二步筛选用的。
至于 Execute就很明显了,做该做的事,比如这里是打印。

创建初始化系统

Systems文件下创建HelloWorldSystem脚本,继承IInitializeSystem,这个脚本是为让程序开始时生成”hello world”,并利用AddDebugMessage将”hello world”改变实体DebugMessage属性,从而触发反应系统。

创建功能

为了方便保持系统组织,需要专门创建一个系统作为一个功能管理其他系统。
所以接下来创建TutorialSystems脚本,继承Feature类,并为这个类制定构造函数,构造函数后面还要跟着: base(“Tutorial Systems”),这样做除了构造父类外,还使用一个string作为功能的name。大概是为了区分功能用(瞎猜的)。

构造函数内利用Add方法加入功能所需要管理的系统,用以制定各系统执行的顺序,这个貌似可以解决unity一些方法执行顺序不清的问题。

MonoBehaviour

终于需要将脚本和unity关联了
创建GameController,负责创建,初始化,执行系统
然后运行系统,hello world

测试反应系统

尝试改变DontDestroyOnLoad /Game下实体的属性,发现每次改变都会输出新的message
尝试手动创建一个实体,更改实体属性,发现2个实体不相互影响

系统清理器

之前在GameController的update函数里加入了_systems.Cleanup();这是专门执行系统清理器的。在系统执行完后销毁系统。

执行系统

Entitas还可以通过这个系统专门监听Input事件来执行要做的事

最后的测试

我给所有系统以及MonoBehaviour加入了log,查看执行顺序
最后的log结果

MonoBehaviour Start() Begin
TutorialSystems Init
HelloWorldSystem Constructor
LogMouseClickSystem Init
DebugMessageSystem GetTrigger
CleanupDebugMessageSystem Init
HelloWorldSystem Init
MonoBehaviour Start() End

初始化正常

MonoBehaviour Update() Begin
DebugMessageSystem Filter
Hello World!
CleanupDebugMessageSystem Clean
MonoBehaviour Update() End

update正常

MonoBehaviour Update() Begin
DebugMessageSystem Filter
DebugMessageSystem Filter
Left Mouse Button Clicked
Right Mouse Button Clicked
CleanupDebugMessageSystem Clean//可以看出清理系统是同时清理了2个
MonoBehaviour Update() End

我们还注意到在DontDestroyOnLoad下存在着可重用实体,当同时点击左右键会有2个可重用实体,Entitas这样做是为减少垃圾回收和内存分配。

Over,Hello World正式结束。
每当学习新技术总是那么令人兴奋,正因为如此才要做程序啊啊。

ECS架构 Entitas-CSharp学习之路(一)相关推荐

  1. Kubernetes学习之路(一)之概念和架构解析和证书创建和分发

    1.Kubernetes的重要概念 转自:CloudMan老师公众号<每天5分钟玩转Kubernetes>https://item.jd.com/26225745440.html Clus ...

  2. 一个DBA老司机的学习之路以及他眼中的Oracle架构演进

    以下整理总结来自盖国强(Eygle)上周四晚云和恩墨大讲堂的分享:我的学习之路和 Oracle 数据库的架构演进,希望能对大家了解 Oracle 的架构演进有所帮助. 大家好,很高兴今天有机会和大家一 ...

  3. C/C++ Linux 后台服务器开发高级架构师学习知识路(架构师篇)

    @[前言: 小编从事c方面10多年的工作经验.今天跟大家分享一下我总结出来的一系列 C/C Linux后台服务器开发的学习路线.从Linux开发工程师-Linux后台开发工程师-Linux高级互联网架 ...

  4. 【云和恩墨大讲堂】盖国强 - Oracle 数据库的架构演进和我的学习之路

    "云和恩墨大讲堂" 线上课程周四晚分享继续.本期我们的分享嘉宾是中国地区首位 Oracle ACE 总监,同时也是云和恩墨创始人 - 盖国强先生.他将围绕两方面主题展开,Oracl ...

  5. ros和java通讯_ROS学习之路(二)——通信架构(上)

    1. master&&node mater中文名又称为节点管理器,作为管家管理所需要的进程,其作用有两个: 每个node启动时都要向master注册. 管理node之间的通信. nod ...

  6. Unity3D学习—牧师与魔鬼—MVC模式和ECS架构应用

    需求 Priests and Devils Priests and Devils is a puzzle game in which you will help the Priests and Dev ...

  7. Unity下的ECS框架 Entitas简介

    最近随着守望先锋制作组在gdc上发布的一个关于ecs的talk,ecs这个架构算是得到了一定的曝光度. 在这之前,github上就一直有一个C#的ecs框架名为Entitas,截止现在已经有1300+ ...

  8. AI 学习之路——轻松初探 Python 篇(三)

    喜欢小之的文章的可以关注公众号「WeaponZhi」持续关注动态 这是「AI 学习之路」的第 3 篇,「Python 学习」的第 3 篇 Python 字符串使用和 C 语言比较类似,但还有一些我们值 ...

  9. 开始了大概三四天的Rails学习之路

    最近因为一位极光推送朋友,我开始了大概三四天的Rails学习之路,最终达到的水平是可以比较轻松地做出大部分功能,然后自我感觉可以自如地按照Rails的设计思想去思考.由于编程的日益流行,我结识了越来越 ...

  10. C/C++学习之路: STL

    C/C++学习之路: STL 目录 STL概述 STL三大组件 常用容器 1. STL概述 STL(Standard Template Library,标准模板库),主要出现在 c++中,但是在引入 ...

最新文章

  1. 谢文睿:西瓜书 + 南瓜书 吃瓜系列 5. 决策树
  2. 小学生数学测试软件c语言流程图,小学生数学测试软件-C语言课程设计
  3. 逆序输出(数组练习)
  4. SpringAOP的Aspectj方式*
  5. android休眠后恢复线程,关于Android系统休眠后,线程的执行情况
  6. Java微服务篇5——Docker
  7. eclipse自动为变量生成Get/Set函数
  8. Linux 简单的shell实现
  9. 嵌入式Linux内核开发工程师必须掌握的三十道题
  10. mysql 查询优化 ~ 善用profie利器
  11. 锋锋5日一更正式开始2021-1-5
  12. 2017年秋季学期获“领跑衫”感言
  13. mac版docker配置加速
  14. Oracle 锁表查询大全
  15. 生信高性能服务器,【玩转腾讯云】使用云服务器进行生信数据分析
  16. 如何在没有安装安卓环境的mac os上装adb环境.
  17. 网易18实习生网测题--吃豆子
  18. 电池管理系统BMS的常见测试方法
  19. [Alg]排序算法之归并排序
  20. 在Linux服务器root用户依然遇到删除不掉得文件如何办 ,宝塔用命令无法删除文件得解决问题,使用rm -rf删除命令提示Operation not permitted 如何解决

热门文章

  1. Red5与Nginx Rtmp性能对比
  2. DevOps 对比分析:产品、服务、开源投入
  3. 百度站点属性怎么设置?PC移动站/独立移动站/自适应/代码适配有什么区别? 404状态码和404页面有什么区别?对SEO有什么影响百度快照投诉不了怎么办(已经解决)提交反馈的按钮变成了灰色
  4. Seo:入门须知(三)网页快照投诉
  5. python爬虫 | 爬取巨潮资讯上的上市公司招股说明书
  6. 为什么DDOS攻击是服务器的最大危害?
  7. FastReport.Net报表工具 vs RDL标准报表定义语言
  8. Vue 项目中各种痛点问题及解决方案
  9. android开发如何获取电话号码的归属地信息
  10. windows应用下面卸载不干净如何解决!