精华内容
参与话题
问答
  • ECS框架

    千次阅读 2018-08-30 17:17:40
    提一下Unity中的Update方法,相信很多开发者都深有体会,引擎中所有的Update是每帧遍历执行的,引擎中的模块多,很多模块我们往往并不使用,此时带来了很大的消耗,可以考虑使用一下ECS框架,可以完美解决这个问题,...

    简介:

    提一下Unity中的Update方法,相信很多开发者都深有体会,引擎中所有的Update是每帧遍历执行的,引擎中的模块多,很多模块我们往往并不使用,此时带来了很大的消耗,可以考虑使用一下ECS框架,可以完美解决这个问题,《守望先锋》游戏正是使用该框架设计。

    ECS编程思想

    这种编程思想很早前就已经提出,ECS分别是Entity,Component,System的缩写. 
    - Entity是实例,作为承载组件的载体,也是框架中维护对象的实体. 
    - Component只包含数据,具备这个组件便具有这个功能. 
    - System作为逻辑维护,维护对应的组件执行相关操作.

    ECS和传统的OOP相比更看重的是组件,附加组件即具备功能.

    C#是面向对象语言,从语言层面上就支持面向对象的编程思想:集成封装和多态.

    • 面向对象:我是什么,我有属性,我有方法,我要继承然后重写成自己独有的.

    • 面向组件:我有什么,我有组件便具备这个功能,我由很多组件组成了实体.

    按照ECS的思路,功能拆分成组件,然后把这些细小的组件组合便组成了具备所需功能的对象.实际上Unity也是这种实现思路,但是它的组件式属性和逻辑的合集.

    最近看了ECS编程思想,看了Entitas逻辑,写了小Demo,感觉:

    编程思路/框架没有最好只有更合适,可能A框架适合RPG游戏,B框架更适合FPS游戏.

    • Entity的时序

      开发中经常会遇到控制时序的逻辑,比如某个Entity必须在另一个Entity逻辑完成后再执行,当然可以通过添加组件标识来控制时序执行,但是如果存在大量这种逻辑势必会增加很多对应的组件作为标识.

    • System时序

      同样可以添加Component作为控制标识作为过滤条件,A系统要在B系统结束之后,C系统启动之前,D系统挂起时…开发到后期怎么办.(待机,前摇,蓄能,施法,表现,结束,)太多太多状态一帧/分帧维护起来估计也会乱吧.

    • System更合适维护一组Entity

      • 比如FPS游戏,MOBA游戏用ECS思想拆分合适,每个玩家的角色功能对等(都是人物,都在移动,战斗)不同的角色的技能枪支拆分单独实现.

      • 如果是大型RPG游戏,每个门派角色,大厅npc,商城商人,敌人角色的状态动作各不相同,这种ECS当然能实现,每个功能,拆分成细小组件,创建对应System类维护,但是这样归根到底一个System经过遍历筛选其实只维护了一个Entity上面的组件.因为角色功能独有,以后这个组件也不会被其他Entity组合使用.

    • 功能解耦

      功能之间解耦,对应的组件和系统只关心本身的功能,不会对其他功能造成影响.

    • 重构

      不方便重构

    • 数据维护

      数据分散到对应的组件,比如服务器下发一份最新数据,需要一一维护对应组件的属性,数据收集也是如此.

    • 客户端服务器共享代码

      客户端拆分表现组件后,只包含纯逻辑功能.

    • 数据层表现层分离

      在不同的组件和系统中维护,不必像oop再拆分表现逻辑.

    • 方便扩展

      如果设计合理,可以通过现有组件组合新的Entity. 
      1- 性能

      数据在内存中线性存在,Entity复用.

    ECS框架:Entitas

    基于ECS的框架有很多,Entitas便是其中一个.

    官方的ECS也会在2018.1beta版本中推出.

    根据wiki很容易就能够搭建起项目,运行起工程,但是有些也需要开发者注意,比如Component作为标识还是作为数据存储;Component生成代码后改变类型或者删除字段后的生成报错,Group/Feature/Unit使用等等.

    关于代码生成,它提供两个版本: 
    - 免费版 
    - 收费版

    免费版根据反射生成代码,需要无编译错误下运行;收费版不需要,即使有代码报错也可以生成代码.

    自己扩展的代码可以分为两部分: 
    - Components 
    - 其他代码(System,Service…等其他逻辑)

    这样做的好处是重新生成代码时,方便的把这两个目录移除项目(或者StreamingAssets),生成基础代码,然后移回Component代码,然后生成组件代码,然后移回其他代码.

    Entitas实践

    最简单的输出hello world

    创建一个Component:

    public class HelloWorldComponent : IComponent
    {
        public string s;
    }
    

    生成代码后创建两个system,一个用来创建entity并附加组件,另一个用来输出日志:

    public class CreatSystem : IInitializeSystem
    {
        private Contexts _contexts;
        public CreatSystem(Contexts contexts)
        {
            _contexts = contexts;
        }
        public void Initialize()
        {
            var e = _contexts.game.CreateEntity();
            e.AddHelloWorld("hello world");
        }
    }
    public class HelloWorldSystem : ReactiveSystem<GameEntity>
    {
        private Contexts _contexts;
    
        public HelloWorldSystem(Contexts contexts) : base(contexts.game){
        }
    
        protected override ICollector<GameEntity> GetTrigger(IContext<GameEntity> context)
        {
            return context.CreateCollector(GameMatcher.HelloWorld);
        }
        protected override bool Filter(GameEntity entity)
        {
            return entity.hasHelloWorld;
        }
        protected override void Execute(List<GameEntity> entities)
        {
            foreach (var e in entities)
            {
                Debug.Log(e.helloWorld.s);
            }
        }
    }

    上面的代码已经实现了输出功能,但是为了方便管理上面的这两个System,我们添加一个RootSystems专门管理游戏中的各种System:

    public class RootSystems : Feature
    {
        public RootSystems(Contexts contexts)
        {
            Add(new CreatSystem(contexts));
            Add(new HelloWorldSystem(contexts));
        }
    }

    所有的功能都实现了,在unity工程中添加一个入口方法挂载到场景中,Main.cs:

    public class Main : MonoBehaviour
    {
        private RootSystems s;
        // Use this for initialization
        void Start()
        {
            s = new RootSystems(Contexts.sharedInstance);
            s.Initialize();
        }
        // Update is called once per frame
        void Update()
        {
            s.Execute();
        }
    }

    运行,正常输出,因为输出实现的是reactivesystem,当值发生变化时便会输出.

    展开全文
  • ecs框架

    2019-01-09 20:18:08
    title: ecs框架 comments: true date: 2019-01-08 18:47:43 tags: 架构 categories: 架构 ECS即Entity-Component-System,实体-组件系统的意思,是一套游戏层的架构,是基于属性的游戏架构.出现很早,随着《守望...

    ECS即Entity-Component-System,实体-组件系统的意思,是一套游戏层的架构,是基于属性的游戏架构.出现很早,随着《守望先锋》架构设计与网络同步 – GDC2017 而进入大众视野,unity2018版本直接内置了ecs,同时新起来的开源游戏引擎CLEngine也内置了ecs。google开源的https://github.com/sschmid/Entitas-CSharp/wiki也是一套ecs框架

    基本思想

    Entity 代表一个独立的个体。
    Component 一个个体某方面的数据,以及它与游戏世界的交互方法。
    System 持续地对Entity上与System对应的Component进行处理,每个System都有独立的更新
    • Entity : 无数据无逻辑,单纯是一个实例,拥有若干Component
    • Component:只有数据没有逻辑,可以被Entity动态添加和移除
    • System:只有逻辑,只关心Component不关心Entity
    • World:缓存所有Entity与Component,并对System进行轮询,负责整个系统的运转

    ECS系统与传统OOP的区别

    在OOP系统中,对象的状态是封装到个体中的,对象会提供一些手段来间接操作这些状态(

    这样的系统其实并没有什么问题,但从游戏开发的角度来讲,当你把一切都封装好并良构之后,游戏设计师突然表示他需要一项和之前完全不一样的功能,而一般灾难都是从这里开始的。

    我们现在知道ECS系统的特征是将状态(由Component提供)与行为(由System系统)进行分离,而Component之间是独立的,我们完全可以通过单独添加新的Component和System来添加新的功能而不修改任何代码,而这也是设计模式中策略模式的思想。

    实例

    Entity
    // AddComponent的功能是在world中创建Component并且添加到该Entity的Component集合中。
    public Entity bullet = new Entity(_entityID);
    bullet.AddComponent(new TransformComponent());
    bullet.AddComponent(new MoveComponent());
    
    Component
    class TransformComponent
    {
        Vector3 position;
        Vector3 rotation;
    }
    class MoveComponent
    {
        Vector3 speed;
    }
    
    System
    class MoveSystem
    {
        public void Update(float time)
        {
            foreach(var movecomponent in movecomponents)
            {
                // 查找该Entity是否拥有TransformComponent
                var transformcomponent = movecomponent.sibling(TransformComponent())
                if(transformcomponent != null)
                    transformcomponent.position += movecomponent.speed * time;
            }
        }
    }
    
    • ECS 模型的结构是非常简洁明晰的,而且由于Component 中只有状态没有逻辑,可以很大的提高 Component 的复用度,以及同类 Component 在内存中是连续分布的,可以很大的提高缓存命中率(关于这点还在想该如何设计数据结构才能达到目的)
    • 对于System需要使用的(整个 world 中唯一的)状态,遵循 System 中无状态的原则,使用 SingletonComponent 的方式去实现(参考 OW ECS 架构中的 SingletonComponent 部分)
    • 将共享的 System 函数分解成 Utility ,减少调用,整合调用点
    • 延迟执行(Deferment)的使用,即先缓存需要执行的状态,在更好的时间点集中调用(这点有很广泛的应用价值)

    例子2

    假设我们有一个绘制函数,这个绘制函数就是一个System,它去会遍历所有带有physical和visible组件的entity,然后利用提供的信息来绘制。

    visible组件内包含有“entity看起来应该是什么样”的信息,而physical组件提供绘制的位置。

    再以碰撞检测为例子。System会遍历所有拥有物理Component的entity,然后对碰撞进行检测并在需要的时候产生对应的事件。

    为Unity设计的ECS系统(Entitas)

    在Unity传统编程中,我们利用MonoBehavior来编写游戏

    核心思想:将数据和行为分开。

    • 在Unity中,我们将MonoBehavior组件放到GameObject中。但Entity系统中不同,Component被设计为附加到Entity上。
    • 使用一个pool来包含所有Entity。通过pool我们可以看到所有的entity。
    • 我们可以对entity进行分组,分组叫做group。之后我们可以通过指定的规则来区分不同的group,
    • 这个规则叫做matcher,通过matcher可以方便地快速获得指定类型的entity。

    cocos使用ecs

    https://www.cnblogs.com/FuTaimeng/p/5572190.html

    展开全文
  • C# ECS框架

    2018-11-06 12:31:30
    C# ECS框架
  • ECS框架初识

    2020-09-18 16:18:22
    ECS框架 Entity(实体) Component(组件) System(系统) ECS框架是一个为了迎合游戏开发,在进几年开始慢慢被推荐熟知的框架,最有代表性的作品就是《守望先锋》 ,其对传统的面向对象设计,组件化思维做了...

    ECS框架

    • Entity(实体)
    • Component(组件)
    • System(系统)

    ECS框架是一个为了迎合游戏开发,在进几年开始慢慢被推荐熟知的框架,最有代表性的作品就是《守望先锋》 ,其对传统的面向对象设计,组件化思维做了进一步的拆解,分成了Entity,Component,System3个部分,三者关系如图:
    在这里插入图片描述

    下面具体说一下这3个部分的功能和特性。

    Entity

    Entity就是我们的游戏世界的各个实体,如人物、枪支、建筑等等最直接的物体原型。无数个Entity构成勒我们整个游戏世界,同样,我们的游戏世界,也就是安装各个规则去运营各个Entity。在传统的面向对象中,一个实体一般是有各种属性,各种功能函数,各种继承、联合等关联的繁杂存在,在ECS框架中,Entity的作品相对简单,ECS会把各种数据拆分对应到各个Component中,Entity根据需求仅需挂载对应属性的Component,而不用关系具体的数据,且各种Component的逻辑处理拆分到对应各个System中,这样,Entity仅是一个包含多个Component的载体。

    举个例子,游戏世界中一辆车,它是一个Entity,它能移动、碰撞、买卖,我们只需挂载对应moveComponent、crashComponent、tradeComponent组件即可,至于各个Component到底存了什么数据,怎么维护的,这都不是Entity需要考虑的,我们只要指定其能移动、碰撞、交易就好了,是不是很简单。

    Component

    做游戏开发的同学肯定对组件比较熟悉,Component是指具有某一个特定功能特性的组件,传统框架中,我们给一个对象挂载一个组件,通常是对对象附加对应的逻辑处理,从而来让对象具有对应的功能,而在ECS中,则把特定的功能特性划分得更彻底,更加强调解耦。对应这个思路,ECS中的Component可理解为专门存储对应特性的数据集合,而对应特性的逻辑,则放到对应的System中去处理。

    仍然以上面的汽车的例子来讨论,汽车要移动,移动的这个特性对应游戏的实现其实是坐标的变化,那对应的数据就有position(x, y),这里我们以二维坐标为例,moveComponent则存放position数据,具体的position的坐标变化逻辑,则放到对应的moveSystem中去处理,我们给汽车挂载moveComponent,这样汽车就能移动勒。

    这里另外要强调一点,在ECS的框架中,特定的Component是要整合到一起的,Component中通常会有一个所有component实例化对象的集合,这样处理的好处就是能达到功能解耦,也能做到利用内存cache,达到更好的性能。后文会再去强调这一点。

    System

    游戏世界中,肯定会有各种各样的规则,我们把各种规则,再细分成各个系统,划分依据是系统最好是功能专一,与其它系统不能耦合,ECS中把这些成为System,且与Component能对应,System主要处理对应Component的逻辑。

    如上面汽车的例子,可能游戏世界,不止汽车会移动,玩家也能奔跑,其也能移动,总之,游戏世界中,所有挂载勒moveComponent的Entity都具有移动的功能,所有的moveComponent的移动逻辑,都归到moveSystem中去处理。

    代码实例

    有了以上的一些初步了解,那对比传统的面向对象,我们剖析一个案例,然后给出大致的代码框架,来更直接对比。
    案例:一个简单的竞速比赛,参数的载具有汽车、摩托车,且每个选手之间能发生碰撞,并掉血,若血量为0,则直接出局。

    传统OOB设计

    class Racer {
      hp: number; // 血量
      crashDecHp: number; // 碰撞扣除血量
      maxSpeed: number; // 最大速度
      accSpeed: number; // 加速度
      posX: number; // 横坐标
      posY: number; // 纵坐标
      // 处理碰撞逻辑
      checkCrash(){ ...}
      // 处理血量逻辑
      updateHp() { ...}
        // 处理移动逻辑
      updatePos() { ...}
    }
    
    class GameManager {
      allRacer: Racer[];
    
      updateGameLogic() {
        allRacer.forEach((racer) => {
          racer.updatePos();
          racer.checkCrash();
          racer.updateHp();
        });
      }
    }
    

    ECS

    // entity和component的映射关系,可以根据自己需求去设计,这里只给出最简单的设计,仅供参考
    class Racer {
    	id: number
    }
    
    class MoveComponent: {
      static allMoveCom: MoveComponent[]; // 所有移动组件集合
      // 在entity上挂载component
      static registerCom(entity: Racer) {
        let newCom = new MoveComponent();
        this.allMoveCom.push(newCom);
      }
      // 销毁整个Component
      static destory();
    
      target: Racer; // 挂载的Entity对象,可根据自己需求设计entity和component的映射关系
      posX: number; // 横坐标
      posY: number; // 纵坐标
      speed: number; // 当前速度
    
      // 注销组件
      cancelComponent();
    }
    
    class MoveSystem {
      static Instance(): MoveSystem;
      updatePos(dt) {
        MoveComponent.allMoveCom.forEach((com) => {
          com.posX += com.speed * dt;
          // ...
        });
      }
    }
    
    class GameManager {
      // 注册System
      registerSystem();
      // 注销System
      cancelSystem();
    
      updateGameLogic() {
        MoveSystem.Instance.updatePos();
        // HPSystem,CrashSystem 类比MoveSystem
      }
    }
    

    ECS的优势

    • 将数据更有效的组织,提高CPU cache利用率
      关于cache的利用率的好处,更多信息大家可以参考ECS 真的是「未来主流」的架构吗?的各位大牛的探讨。
    • 逻辑更彻底的解耦,便于并行、扩展、整合
      逻辑解耦是所以框架都希望的解决的。传统的框架也是力求将游戏能拆分更好更干净的模块。

    总结

    ECS的并不是一个突如其来的框架,其思想在传统的游戏开发中多少都会运用到,比如“输入指令”,“网络模块”,各种单例的Manager类等等,其思想很接近ECS,只不过他们更多的吧ECS整合到一个类中,即保存数据,又处理逻辑,能完成单一独立的逻辑。ECS的思想则是看到这种设计的好处,把其严格的扩展到项目的所有逻辑处理中,所有的逻辑都拆分成System,需要参与其中的Entity挂载对应Component,这样项目的逻辑就非常解耦,便于扩充,维护。

    一个东西出现,很容易被其亮眼的地方所吸引,但同时我们也要多思考其是否真的那么完美,这里推荐大家阅读下《ECS 真的是「未来主流」的架构吗?》,不一定要为了ECS就强行套用,等自己发现项目的System逻辑臃肿,自己也分不清怎么去解耦时,给自己带来的反而是困扰。

    ECS本人并没有实际的项目经验,对其的理解可能也是停留在很肤浅的表面,本文的论述仅供参考,欢迎探讨指正。

    参考资料

    ECS 真的是「未来主流」的架构吗?
    浅谈《守望先锋》中的 ECS 构架
    游戏开发中的ECS 架构概述

    展开全文
  • Lua版ECS框架实现

    2018-07-27 12:04:13
    Lua版ECS框架实现,ECS编程思想,这种编程思想很早前就已经提出,ECS分别是Entity,Component,System的缩写. - Entity是实例,作为承载组件的载体,也是框架中维护对象的实体. - Component只包含数据,具备这个组件便...
  • ECS框架的初步探究

    千次阅读 2018-03-19 17:12:10
    欢迎参与讨论,转载请注明出处。 ... 前言  在阅读这篇文章之前,你需要了解一下何为ECS框架。关于ECS框架,其实近年来... ECS框架的存在实际上很早就出现了(我记得最初在2003年),近年随着《守望先锋》架构设...

      欢迎参与讨论,转载请注明出处。
      本文转载自:https://musoucrow.github.io/2018/03/19/ecs_first/

    前言

      在阅读这篇文章之前,你需要了解一下何为ECS框架。关于ECS框架,其实近年来一直想去尝试,终于在近日有所体悟,遂有此文。

    详解

      ECS框架的存在实际上很早就出现了(我记得最初在2003年),近年随着《守望先锋》架构设计与网络同步一文出现后瞬间成了炙手可热的新星。
      ECS框架与帧同步锁定类似,皆只是拥有一个概念,但无确切的实现标准。但事实上已经不少现成的实现(如Entitas),不过我觉得Entitas在与Unity的结合上不符合我的审美,于是自己动手造了个轮子。
      ECS框架的概念其实相当直观:Entity-Component-System三件套。
    * Entity即实体,作为Component的经纪人,可拥有多个Component。
    * Component即组件,作为数据存储的容器,原则上只包含内部数据自处理的函数。Component以Entity作为标识,以此判断所属。
    * System即系统,作为业务函数的集合,会与Component对接实现业务运行(System处理Component)。

      以上三点可谓看过相关文章的都懂,只是落实到具体实现上仍会有不少不明不白之处(Entity是作为容器还是标识符?Component可否嵌套Component?System之间可否相互调用?)。以上问题并没有确切的答案,只能是落实实现时根据需求而定。

    实现

      所谓实践出真知,在此之前我写了个贪吃蛇,这是个不错的素材,于是便将其ECS化。这下也可将两者进行对比,品味其中区别。

    Entity

      由于这款游戏是使用Unity制作的,那么自然最好与Unity本身相结合。我首先考虑到的便是与Unity本身的GameObject-Behavior(其实是Component,为防误解,特此改称)框架结合(业务环境下有调用它们的需求),于是选择将Entity做成一个Behavior:

    using System;
    using UnityEngine;
    
    namespace Game.Core {
        public class Entity : MonoBehaviour {
            public static event Action<Entity> NewTickEvent;
            public static event Action<Entity> DestroyTickEvent;
    
            protected void Start() {
                if (Entity.NewTickEvent != null) {
                    Entity.NewTickEvent(this);
                }
            }
    
            protected void OnDestroy() {
                if (Entity.DestroyTickEvent != null) {
                    Entity.DestroyTickEvent(this);
                }
            }
        }
    }

      可以看出,Entity的生命周期也与GameObject进行了捆绑,并且设置了两个event令System可以进行监控。
      再来看看Entity的具体实例:

    using UnityEngine;
    
    namespace Game.Entitys {
        using Core;
        using Components;
    
        public class Food : Entity {
            public Position position = new Position();
    
            protected void Awake() {
                this.position.Init(this);
            }
    
            protected new void OnDestroy() {
                base.OnDestroy();
    
                this.position.Destroy();
            }
        }
    }

    food

      可以看出Food实体创建了一个Position组件,托Unity编辑器的服,我们可以清晰地看到Position的数据构成,并可方便地进行编辑(包括运行时)。当然可以看得出这里Component的创建方式相当别扭(实例化后仍需Init),这是为了对接Unity的序列化功能,若不这么做的话,某些数据将会序列化失败(如Collision Slot)。

    Component

      Component的初始实现便很简单了,只需要对接Entity以及预留Init与Destroy接口即可:

    using System;
    
    namespace Game.Core {
        [Serializable]
        public class Component {
            [NonSerialized]
            public Entity entity;
    
            public virtual void Init(Entity entity) {
                this.entity = entity;
            }
    
            public virtual void Destroy() {}
        }
    }

      这里令Component拥有entity是为了便于识别身份,[Serializable]标识表示该对象可序列化(与编辑器交互),[NonSerialized]标识表示不让该变量序列化(没有显示在编辑器的需求)。接下来看看Position组件的具体实现:

    using System;
    using System.Collections.Generic;
    using UnityEngine;
    
    namespace Game.Components {
        using Core;
        using Solts;
    
        public class Position : Component {
            public static Dictionary<Entity, Position> Map = new Dictionary<Entity, Position>();
            public static List<Position> List = new List<Position>();
    
            public Vector2Int value;
            public Collision collsionSlot;
    
            public override void Init(Entity entity) {
                base.Init(entity);
    
                Position.Map.Add(entity, this);
                Position.List.Add(this);
            }
    
            public override void Destroy() {
                Position.Map.Remove(this.entity);
                Position.List.Remove(this);
            }
        }
    }

      关于ECS框架有一个很普遍的问题:在System要如何获取到Component?我的解决方法便是为有获取需求的Component设立存储容器,当然这种写法有点死板,应该专门设立容器管理类进行自动化处理,这是个可改善的方向。

    System

      System纯粹来看便是个函数集,在Entitas的实现是专门设立Behavior装载System以运行。而我选择分离:System即Behavior,两者倒没什么根本上的区别,全凭个人喜好罢了。在以Behavior的实现下并不需要System基类,以下以涉及到坐标与碰撞的Field系统为例:

    using System.Collections.Generic;
    using UnityEngine;
    
    namespace Game.Systems {
        using Core;
        using Components;
        using Entitys;
    
        public class Field : MonoBehaviour {
            public const float SIZE = 0.32f;
    
            private static List<Entity> SyncList = new List<Entity>();
    
            public static Vector2 ToPosition(int x, int y) {
                return new Vector2(x * SIZE + SIZE * 0.5f, y * SIZE + SIZE * 0.5f);
            }
    
            public static void AdjustPosition(Position position, Transform transform=null) {
                transform = transform == null ? position.entity.transform : transform;
                transform.position = Field.ToPosition(position.value.x, position.value.y);
            }
    
            private static void Collide(Position a, Position b) {
                if (a.value == b.value) {
                    if (a.collsionSlot != null) {
                        a.collsionSlot.Run(a.entity, b.entity);
                    }
    
                    if (b.collsionSlot != null) {
                        b.collsionSlot.Run(b.entity, a.entity);
                    }
                }
            }
    
            private static void Sync(Position position, Joint joint) {
                joint.laterPos = position.value;
            }
    
            protected void Awake() {
                Entity.NewTickEvent += this.NewTick;
                Entity.DestroyTickEvent += this.DestroyTick;
                Director.UpdateTickEvent += this.UpdateTick;
            }
    
            private void NewTick(Entity entity) {
                bool hasPos = Position.Map.ContainsKey(entity);
                bool hasJoi = Joint.Map.ContainsKey(entity);
    
                if (hasPos) {
                    Field.AdjustPosition(Position.Map[entity]);
                }
    
                if (hasPos && hasJoi) {
                    Field.SyncList.Add(entity);
                }
            }
    
            private void UpdateTick() {
                for (int i = 0; i < Position.List.Count; i++) {
                    for (int j = i + 1; j < Position.List.Count; j++) {
                        Field.Collide(Position.List[i], Position.List[j]);
                    }
                }
    
                foreach (var entity in Field.SyncList) {
                    Field.Sync(Position.Map[entity], Joint.Map[entity]);
                }
            }
    
            private void DestroyTick(Entity entity) {
                if (Field.SyncList.Contains(entity)) {
                    Field.SyncList.Remove(entity);
                }
            }
        }
    }

      可以看出,继承Behavior的System可以很方便地使用自带的各种回调函数(如Awake),业务函数也变得清晰无比,只需要提供相应Component即可(如AdjustPosition)。对于一些需要复合组件的业务(如Sync),则会专门设立容器(SyncList)进行存储,对Entity的NewTickEvent与DestroyTickEvent进行监控便可筛选出合适的对象,且所有组件可通过Entity从组件容器进行获取,十分方便。
      当然也不要忘记与编辑器结合的优势,System也可以将变量序列化与编辑器交互:
    system
      当然Unity可进行序列化的部分只有实例变量,所以需要作此处理:

    public class Test : MonoBehaviour {
        private static Test Instance;
    
        public static int Get() {
            return Instance.value;
        }
    
        public int value;
    
        protected void Awake() {
            Food.Instance = this;
        }
    }

      因为System是单例Behavior,所以这么做是安全的。如此便可操作实例对象了。

    后记

      总的而言,ECS框架主要是一种对OOP思想的反思,甚至可以说是一种复古(函数式编程风格)。也是一种彻底的组件模式实现,彻底地奉行数据-逻辑分离。它使得我们更容易地去抽象、描述游戏事物。当然我认为它在某种程度上是反直觉的、抽象的(某些只会属于某个对象所属的业务却要分开写,并且用组件去涵盖)。所以我认为它更适用于某些场景下,如动作游戏里的地图单位,分为多种样式(物件、道具、战斗单位、NPC、飞行道具等),这种时候使用传统的继承+子对象写法确实不如ECS来得好了。再比如UI方面,我认为还是MVC框架更为王道。所以切忌教条主义,一切跟着实际需求走。

    展开全文
  • Lua下的ECS框架

    2020-12-21 10:11:11
    最近想试试在 Lua 中实现一个简单的 ECS 框架,又仔细琢磨了一下。 我思考后的结论是:ECS 并不是一个新概念,它的提出其实是和语言相关的。ECS 概念的诞生起于游戏行业,相关框架基本都是基于 C++ 来开发的。它其实...
  • 关于ecs框架的入门

    千次阅读 2018-12-07 14:53:20
    ecs框架,是面向组件的编程 ecs框架 实体Entity:是一个ID,用于组件的间接查找,用于在容器里遍历,相当于是unity里的对象 ComponentData 组件数据:用于存放组建里的数据 ComponentSystem 组件系统:用于存放...
  • ECS框架学习-01

    2021-01-08 13:54:37
    ECS框架学习-01 移动功能实现步骤: 创建位置组件(Component),速度组件。(注:这里采用的移动方法是数学计算,位移 = 速度*时间) 创建实体(Entity),添加组件 创建移动系统(system),通过GameMatcher查找具有...
  • 使用中的 ECS 框架

    2019-04-19 13:26:08
    在这之前,github上就一直有一个C#的ecs框架名为Entitas,截止现在已经有1300+的star了,同时提供了和unity整合的方法(对,你可以不用unity,直接把它当C#的库来做其他的东西) 地址:...
  • Unity3D ECS框架 Entitas 基本概念

    千次阅读 2018-10-08 16:59:48
    Unity3D ECS框架 Entitas 基本概念 GeniusCode 游戏开发 3 人赞了该文章 原文链接:Unity3D ECS框架 基本概念 Entitas是一个非常好用的框架,高复用性的代码,对Unity3D进行高度优化,大量的缓存对象管理,...
  • Unity2018 ECS框架简明教程 &amp;nbsp;简介 全新的Unity官版ECS在Unite2017 Austin就已经show过了,主要特征如下: 数据和行为分离 在通常的Unity开发中,我们会将Monobehavior组件挂载到一个Gameobjec上...
  • 以下文档均来源于ECS官网: ... 使用EntityQuery查询数据 ... ECS框架中的数据是存储在组件中的,组件根据它们所属实体的原型,在内存中被组合在一起。 要定义ECS的数据视图,让其仅包含给你需要计算...
  • ECS框架学习(Entitas)入门

    千次阅读 2019-03-18 16:05:50
    公司要求使用ECS框架经行开发,没办法只能自己学习了。记录一下学习过程。 框架的含义可以去其他地方查看,我这里写一点我的理解(新人,不对请谅解)。ECS即Entity-Component-System(实体-组件-系统) 的缩写。它...
  • ECS(Entity,Component,System)架构其实已经不是新鲜事物,只是在GDC 2017守望先锋讲座后,才真正流行或者说是被大众所知,我接触已经是非常晚的2019年,Unity 出了自带ECS框架。守望先锋使用ECS是用来降低不停增长的...
  • Unity ECS 框架

    万次阅读 2018-03-12 11:35:19
    - Entity是实例,作为承载组件的载体,也是框架中维护对象的实体. - Component只包含数据,具备这个组件便具有这个功能. - System作为逻辑维护,维护对应的组件执行相关操作. ECS和传统的OOP相比更看重的是组件,附加...
  • 以下文档均来源于ECS官网: ... 你可以在JobComponentSystem中定义IJobForEach作业以读写组件数据。当此Job运行时,ECS框架会找出具有所需组件的所有实体,并为每个实体调用Job的Execute()方法。数据...
  • ECS框架学习

    2019-12-04 15:54:06
    ECS在各种Unity版本上表现都不一样,官方给的例子如果用Unity2018.3.1打开就会满处飘红。坑很多,官方文档像屎一样。好在有大神们在,看了很多博客,终于找到一个能用的,完成了一个小demo,十万个小立方体围着中间转...
  • Unity ECS框架

    2019-06-25 17:15:39
    下面是我在初步学习Unity ECS编程模式,对我比较有帮助的几篇文章。先把资源放在这里防止忘记,也希望能帮助有需要的人。 等对ECS又了自己比较清晰的认知以后,再过来更新。 ...
  • 在这之前,github上就一直有一个C#的ecs框架名为Entitas,截止现在已经有1300+的star了,同时提供了和unity整合的方法(对,你可以不用unity,直接把它当C#的库来做其他的东西) 地址: ...
  • 因为demo是基于ECS框架,所以如果不了解ECS框架的话,可以看看我的ECS框架系列文章,获取我已经开源的ECS框架。或者扫文末的二维码联系我,给我“加个鸡腿”支持一下!19.9 交个朋友。 正文 最终效果 先给大家看看...
  • 本文主题:ECS框架的启动和循环,System的OnUpdate函数在哪里被调用,运行时disable一个System 先上个简约版的类图: World:保存着一个EntityManager和一堆ComponentSystem,默认情况下游戏启动时会为我们创建...
  • 在H5下使用ECS框架; 用ECS框架与四叉树结合来辅助物体之间碰撞检测; 因为东西比较多,所以我打算分2篇文章来介绍,本篇我们就来介绍下如何在h5下使用ECS框架。 正文 最终效果 先给大家看看demo运行起来的效果,看...

空空如也

1 2 3 4 5 ... 20
收藏数 428
精华内容 171
关键字:

ecs框架