这一章开始实现案例最后一个功能,实时打印小熊的位置信息。可能你会问打印位置信息和Entitas有什么关系,一句Debug.Log()不就完事了,是的!如果是在Unity环境下是这样一句话就行了,但是Entitas的开发环境是不依赖于Untiy的,这样的好处是如果有服务器和客户端公用一套战斗逻辑的时候,Entitas从客户端移植到服务器就会很方便,但是因为刚才在代码中插入了Debug.Log()导致需要修改源码或者服务器引入UnityEngine.dll。
ICleanupSystem
这个系统主要是在每帧的逻辑执行结束之后执行Cleanup()做一些清理工作,是的,这个系统也是每帧执行一次,那么它和IExecuteSystem又有什么区别呢。你可以对照一下MonoBehaviour生命周期中的Update()和LateUpdate()的区别。
所以Systems.Execute()调用之后才能调用Systems.Cleanup()方法。或者干脆将Systems.Cleanup()方法放在LateUpdate()中调用。
LogMessageComponent.cs
首先我们需要一个LogMessageComponent用来记录需要打印的数据信息。
using Entitas;
/// <summary>
/// LogMessage组件
/// </summary>
[Game]
public class LogMessageComponent : IComponent
{
public string message;
}
ShowLogMessageSystem.cs ReactiveSystem
然后通过一个ReactiveSystem来找到那些有LogMessageComponent的Entity并将message打印出来。
这样的好处是如果需要移植到服务器全局只需要修改这一个地方的打印方法即可。
using Entitas;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// ShowLogMessage系统
/// </summary>
public class ShowLogMessageSystem : ReactiveSystem<GameEntity>
{
public ShowLogMessageSystem(Contexts contexts) : base(contexts.game)
{
}
protected override void Execute(List<GameEntity> entities)
{
foreach (var item in entities)
{
Debug.Log(item.logMessage.message);
item.isDestoryLog = true;
}
}
protected override bool Filter(GameEntity entity)
{
return entity.hasLogMessage && !entity.isDestoryLog;
}
protected override ICollector<GameEntity> GetTrigger(IContext<GameEntity> context)
{
return context.CreateCollector(GameMatcher.LogMessage);
}
}
还需要在RenderPositionSystem中刷新GameObject位置的时候创建一个拥有LogMessageComponent的Entity
public class RenderPositionSystem : ReactiveSystem<GameEntity>
{
...
protected override void Execute(List<GameEntity> entities)
{
foreach (var item in entities)
{
//为每个符合条件的Entity刷新位置
item.view.gameObject.transform.position = item.position.value;
//创建一个Entity并添加LogMessageComponeent
_context.CreateEntity().AddLogMessage(string.Format("position = {0},{1}",item.position.value.x, item.position.value.y));
}
}
...
}
注意:因为这里创建的GameEntity只有LogMessageComponent,没有ViewComponent, SpriteComponent等所以不会被RenderPositionSystem等其他System处理。
最后将ShowLogMessageSystem.cs添加到GameSystems中。
public class GameSystems : Feature
{
public GameSystems(Contexts contexts) : base("Game Systems")
{
...
Add(new ShowLogMessageSystem(contexts));
}
}
回到Unity编辑器运行一下,可以在控制台看到打印信息了,但是有一个很可怕的情况出现了,
随着我们的运行GameEntity越来越多,这些Entity会创建只是包含了我们的输出信息,之后就再也没用了,那么我们需要在信息被输出之后将这个GameEntity清楚掉。
DestoryLogMessageSystem.cs ICleanupSystem
我们在ShowLogMessageSystem中会输出信息,ShowLogMessageSystem是继承ReactiveSystem,所以可以在ICleanupSystem中清理所有拥有LogMessageComponent的实体,因为ReactiveSystem实现了IExecuteSystem接口的Execute()方法,而前面我们也说过了ICleanupSystem 的执行会晚于IExecuteSystem。
using Entitas;
/// <summary>
/// DestoryLogMessage系统
/// </summary>
public class DestoryLogMessageSystem : ICleanupSystem
{
readonly GameContext _context;
readonly IGroup<GameEntity> messageEntity;
public DestoryLogMessageSystem(Contexts contexts)
{
_context = contexts.game;
messageEntity = _context.GetGroup(GameMatcher.LogMessage);
}
public void Cleanup()
{
foreach (var item in messageEntity.GetEntities())
{
item.Destroy();
}
}
}
最后再将DestoryLogMessageSystem.cs添加到GameSystem中,并再GameController的LateUpdate()方法中执行Systems的Cleanup()方法
public class GameController : MonoBehaviour
{
...
void LateUpdate()
{
_systems.Cleanup();
}
}
再次回到Unity运行,现在既可以输出信息,GameEntity也始终只会有两个,一个是小熊,一个是Log。并且当左右按键都松开的时候表示Log的Entity会被回收。
总计: ICleanupSystem也是每帧执行的系统。不过从逻辑上区分这个system是为了我们在执行完所有的IExecuteSystem之后执行的逻辑所设立的,用来做一些数据的清理的作用.需要重写void Cleanup()方法; 将清理逻辑写在这个方法中。