总起:
今天主要承接上一节的内容来实现点击右键创建角色、点击左键移动角色的功能。
这边会在IComponent中保存Unity场景中GameObject的引用,以便在各个System中使用,并使用Link方法可以在场景中的看到调试信息。
如果你第一次学习该内容,请根据第二节内容完成input相关的代码(主要EmitInputSystem)。这里我提供一下已经完成的工程:Entitas简单移动项目。
GameComponents:
这边提供了所有后面要用到的Components,直接看代码吧:
-
- using UnityEngine;
- using Entitas;
-
-
- [Game]
- public sealed class PositionComponent : IComponent
- {
- public Vector2 value;
- }
-
-
- [Game]
- public class DirectionComponent : IComponent
- {
- public float value;
- }
-
-
- [Game]
- public class ViewComponent : IComponent
- {
- public GameObject gameObject;
- }
-
-
- [Game]
- public class SpriteComponent : IComponent
- {
- public string name;
- }
-
-
- [Game]
- public class MoverComponent : IComponent
- {
- }
-
-
- [Game]
- public class MoveComponent : IComponent
- {
- public Vector2 target;
- }
-
-
- [Game]
- public class MoveCompleteComponent : IComponent
- {
- }
Component的数量有点多,这边要注意自己在写的时候,尽量将Component分的细一些,一个功能对应一个Component,这样在写System的时候会很舒服,自然而然就出来了。
以上的代码写完之后,按住Ctrl Shift,再按一下G,生成Component对应的Entitas代码。
点击右键产生一个移动者:
首先我们需要在检测到InputContext有右键按下时,就在GameContext中生成一个代表移动者的GameEntity:
-
- using System.Collections.Generic;
- using Entitas;
- using UnityEngine;
-
-
- public class CreateMoverSystem : ReactiveSystem<InputEntity>
- {
- readonly GameContext _gameContext;
- public CreateMoverSystem(Contexts contexts) : base(contexts.input)
- {
- _gameContext = contexts.game;
- }
-
-
- protected override ICollector<InputEntity> GetTrigger(IContext<InputEntity> context)
- {
- return context.CreateCollector(InputMatcher.AllOf(InputMatcher.RightMouse, InputMatcher.MouseDown));
- }
-
-
- protected override bool Filter(InputEntity entity)
- {
- return entity.hasMouseDown;
- }
-
-
- protected override void Execute(List<InputEntity> entities)
- {
- foreach (InputEntity e in entities)
- {
- GameEntity mover = _gameContext.CreateEntity();
- mover.isMover = true;
- mover.AddPosition(e.mouseDown.position);
- mover.AddDirection(Random.Range(0, 360));
- mover.AddSprite("head1");
- }
- }
- }
以上的创建Entity代码处理完,下面就是根据Mover标志、Position、Direction等编写对应的System处理具体的情况。
在Unity中表现一切都要基于GameObject,所以首先第一步就是创建GameObject:
-
- using System.Collections.Generic;
- using Entitas;
- using Entitas.Unity;
- using UnityEngine;
-
-
- public class AddViewSystem : ReactiveSystem<GameEntity>
- {
-
- readonly Transform _viewContainer = new GameObject("Game Views").transform;
- readonly GameContext _context;
-
- public AddViewSystem(Contexts contexts) : base(contexts.game)
- {
- _context = contexts.game;
- }
-
-
- protected override ICollector<GameEntity> GetTrigger(IContext<GameEntity> context)
- {
- return context.CreateCollector(GameMatcher.Sprite);
- }
-
-
- protected override bool Filter(GameEntity entity)
- {
- return entity.hasSprite && !entity.hasView;
- }
-
-
- protected override void Execute(List<GameEntity> entities)
- {
- foreach (GameEntity e in entities)
- {
- GameObject go = new GameObject("Game View");
- go.transform.SetParent(_viewContainer, false);
- e.AddView(go);
- go.Link(e, _context);
- }
- }
- }
将以上的System都添加到System组中运行就可以看到效果了。右键点击,在Game Views的父节点就会添加一个Game View节点。
在上面的代码中不写go.Link(e,_context)这行完全也是可以的,这行的目标就是为了调试方便,能直接在节点中看到关联的Entity的状况:

节点是有了,但没有图片显示总归是不爽,接下来就搞Sprite渲染的System:
-
- using System.Collections.Generic;
- using Entitas;
- using UnityEngine;
-
- public class RenderSpriteSystem : ReactiveSystem<GameEntity>
- {
- public RenderSpriteSystem(Contexts contexts) : base(contexts.game)
- {
- }
-
-
- protected override ICollector<GameEntity> GetTrigger(IContext<GameEntity> context)
- {
- return context.CreateCollector(GameMatcher.Sprite);
- }
-
- protected override bool Filter(GameEntity entity)
- {
- return entity.hasSprite && entity.hasView;
- }
-
-
-
-
- protected override void Execute(List<GameEntity> entities)
- {
- foreach (GameEntity e in entities)
- {
- GameObject go = e.view.gameObject;
-
-
- SpriteRenderer sr = go.GetComponent<SpriteRenderer>();
- if (sr == null) sr = go.AddComponent<SpriteRenderer>();
-
- sr.sprite = Resources.Load<Sprite>(e.sprite.name);
- }
- }
- }
写完一添加System,效果就出来了:

当然没有对Position和Direction进行处理,所以生成的所有GameView都在中间,也就是(0, 0)位置。
OK,接下来就是对Position和Direction进行处理,一样注意需要放在AddViewSystem之后添加System:
-
- using System.Collections.Generic;
- using Entitas;
-
-
- public class RenderPositionSystem : ReactiveSystem<GameEntity>
- {
- public RenderPositionSystem(Contexts contexts) : base(contexts.game)
- {
- }
-
- protected override ICollector<GameEntity> GetTrigger(IContext<GameEntity> context)
- {
- return context.CreateCollector(GameMatcher.Position);
- }
-
- protected override bool Filter(GameEntity entity)
- {
- return entity.hasPosition && entity.hasView;
- }
-
- protected override void Execute(List<GameEntity> entities)
- {
- foreach (GameEntity e in entities)
- {
- e.view.gameObject.transform.position = e.position.value;
- }
- }
- }
-
-
- using System.Collections.Generic;
- using Entitas;
- using UnityEngine;
-
-
- public class RenderDirectionSystem : ReactiveSystem<GameEntity>
- {
- readonly GameContext _context;
-
- public RenderDirectionSystem(Contexts contexts) : base(contexts.game)
- {
- _context = contexts.game;
- }
-
- protected override ICollector<GameEntity> GetTrigger(IContext<GameEntity> context)
- {
- return context.CreateCollector(GameMatcher.Direction);
- }
-
- protected override bool Filter(GameEntity entity)
- {
- return entity.hasDirection && entity.hasView;
- }
-
- protected override void Execute(List<GameEntity> entities)
- {
- foreach (GameEntity e in entities)
- {
- float ang = e.direction.value;
- e.view.gameObject.transform.rotation = Quaternion.AngleAxis(ang - 90, Vector3.forward);
- }
- }
- }
好了,添加System再运行:

完美!
做到这里是不是有这样的感觉:一个Component对应一个System,Component的内容进行改变时就由System进行处理。如果去掉RenderDirectionSystem,则它的Direction不会起效,所有图片就都会正着显示;如果去掉RenderPositionSystem,则Position就不会起效。
嗯,有这样的感觉就说明掌握了Entitas的基本用法,Component和ReactiveSystem相对应进行使用。是其框架的主要思想,但也不仅仅局限于此,接下来的两个System用于完成点击左键,图片移动的功能。
首先Entitas的Context本身就是个消息池,或者说是NotificationCenter,所以这边要点击左键发出命令进行移动就特别方便。
我们首先来看创建命令的System:
-
- using System.Collections.Generic;
- using Entitas;
-
-
- public class CommandMoveSystem : ReactiveSystem<InputEntity>
- {
- readonly IGroup<GameEntity> _movers;
-
-
- public CommandMoveSystem(Contexts contexts) : base(contexts.input)
- {
- _movers = contexts.game.GetGroup(GameMatcher.AllOf(GameMatcher.Mover));
- }
-
-
- protected override ICollector<InputEntity> GetTrigger(IContext<InputEntity> context)
- {
- return context.CreateCollector(InputMatcher.AllOf(InputMatcher.LeftMouse, InputMatcher.MouseDown));
- }
-
- protected override bool Filter(InputEntity entity)
- {
- return entity.hasMouseDown;
- }
-
-
- protected override void Execute(List<InputEntity> entities)
- {
- foreach (InputEntity e in entities)
- {
- GameEntity[] movers = _movers.GetEntities();
- foreach (GameEntity entity in movers)
- entity.ReplaceMove(e.mouseDown.position);
- }
- }
- }
添加System,并运行,添加几个Mover,并在屏幕上点击左键时,就会在Game View的Entity Link中就可以看到Move组件,并会随着点击其值发生变化:

直接看上去没有变化的效果,是因为没有刷新,你可以先切换到其他节点,再切换回原节点就能看到。
最后一步是移动:
-
- using Entitas;
- using UnityEngine;
-
-
-
- public class MoveSystem : IExecuteSystem, ICleanupSystem
- {
- readonly IGroup<GameEntity> _moves;
- readonly IGroup<GameEntity> _moveCompletes;
- const float _speed = 4f;
-
-
- public MoveSystem(Contexts contexts)
- {
- _moves = contexts.game.GetGroup(GameMatcher.Move);
- _moveCompletes = contexts.game.GetGroup(GameMatcher.MoveComplete);
- }
-
-
- public void Execute()
- {
- foreach (GameEntity e in _moves.GetEntities())
- {
-
- Vector2 dir = e.move.target - e.position.value;
- Vector2 newPosition = e.position.value dir.normalized * _speed * Time.deltaTime;
- e.ReplacePosition(newPosition);
-
-
- float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
- e.ReplaceDirection(angle);
-
-
- float dist = dir.magnitude;
- if (dist <= 0.5f)
- {
- e.RemoveMove();
- e.isMoveComplete = true;
- }
- }
- }
-
-
- public void Cleanup()
- {
- foreach (GameEntity e in _moveCompletes.GetEntities())
- {
- e.isMoveComplete = false;
- }
- }
- }
OK,添加System,并运行,效果就出来了:

点击中键更换图片:
这个功能大家想想看该怎么做,能把这个功能做出来的话Entitas就基本掌握了,没有什么思路的话请看看我的方法,再揣摩揣摩:
-
- using UnityEngine;
- using Entitas;
-
- public class MiddleMouseKeyChangeSpriteSystem : IExecuteSystem
- {
- readonly IGroup<GameEntity> _sprites;
-
-
- public MiddleMouseKeyChangeSpriteSystem(Contexts contexts)
- {
- _sprites = contexts.game.GetGroup(GameMatcher.Sprite);
- }
-
-
- public void Execute()
- {
- if(Input.GetMouseButtonDown(2))
- {
- foreach(var e in _sprites.GetEntities())
- {
- e.sprite.name = "head2";
- }
- }
- }
- }
添加到System组中,在运行,效果就出来了……那是不可能的。
这边讲一个知识点,就是你在调试模式下,在Inspector调整Entity的Component,比如说是Sprite,执行的是e.ReplaceSprite方法。也就是说e.sprite.name = "head2"并不会触发ReactiveSystem,这点需要注意。
把上面的e.sprite.name = "head2"改成e.ReplaceSprite("head2")就行了。
再试试吧。
最终效果:

来自:http://blog.csdn.net/u012632851/article/details/77867799