任务7:TimeTestComponent-组件事件系统练习
TimeTestComponent组件纯练习了解et组件与事件用,没实际用途。
这个组件练习的目标是:
- 分离一些可能用到的时间相关的行为,可以单独定义这些时间行为
- 可以给实体添加TimeTestComponent组件,可以调用需要的时间行为
- 可以用统一的接口方法调用时间行为
添加一个测试实体TestRoom
\Assets\Model\Test05\TestRoom.cs
using System.Threading; namespace ETModel { public sealed class TestRoom : Entity { public CancellationTokenSource waitCts; } }
添加ITimeBehavior时间行为接口
\Assets\Model\Test05\ITimeBehavior.cs
using System; namespace ETModel { [AttributeUsage(AttributeTargets.Class)] public class TimeBehaviorAttribute: BaseAttribute { public string Type { get; } public TimeBehaviorAttribute(string type) { this.Type = type; } } public static partial class Typebehavior{ public const string Waiting = "Waiting"; public const string RandTarget = "RandTarget"; } public interface ITimeBehavior { void Behavior(Entity parent,long time); } }
添加一个WaitingTimeBehavior时间行为对象
\Assets\Model\Test05\WaitingTimeBehavior.cs
加TimeBehavior特性
继承ITimeBehavior接口
实现测试实体需要的某种等待行为
using ETModel; using System; using System.Threading; using System.Threading.Tasks; namespace ETModel { [TimeBehavior(Typebehavior.Waiting)] public class WaitingTimeBehavior : ITimeBehavior { public TestRoom room; public long waitTime; public void Behavior(Entity parent,long time) { room = parent as TestRoom; waitTime = time; Waiting().Coroutine(); } public async ETVoid Waiting(){ TimerComponent timer = Game.Scene.GetComponent<TimerComponent>(); room.waitCts = new CancellationTokenSource(); await timer.WaitAsync(waitTime,room.waitCts.Token); Log.Info($"{room.GetType().ToString()}-执行完waiting"); room.waitCts.Dispose(); room.waitCts = null; } } }
添加TimeTestComponent组件
\Assets\Model\Test05\TimeTestComponent.cs
时间行为的调用方法
public void Run(string type,long time=0){ try { Tbehaviors[type].Behavior(parent,time); } catch (Exception e) { throw new Exception($"{type} Time Behavior 错误: {e}"); } }
我们把单独定义的时间方法对象实例存在Tbehaviors 字典中
private readonly Dictionary<string, ITimeBehavior> Tbehaviors = new Dictionary<string, ITimeBehavior>();
扫描程序集中定义的时间行为对象添加到Tbehaviors 字典中
//扫描程序集,获取所有具有[TimeBehavior]特性的对象 //都添加到Tbehaviors中 public void Load() { this.Tbehaviors.Clear(); List<Type> types = Game.EventSystem.GetTypes(typeof(TimeBehaviorAttribute)); foreach (Type type in types) { object[] attrs = type.GetCustomAttributes(typeof (TimeBehaviorAttribute), false); if (attrs.Length == 0) { continue; } TimeBehaviorAttribute attribute = attrs[0] as TimeBehaviorAttribute; if (Tbehaviors.ContainsKey(attribute.Type)) { Log.Debug($"已经存在同类Time Behavior: {attribute.Type}"); throw new Exception($"已经存在同类Time Behavior: {attribute.Type}"); } object o = Activator.CreateInstance(type); ITimeBehavior behavior = o as ITimeBehavior; if (behavior == null) { Log.Error($"{o.GetType().FullName} 没有继承 ITimeBehavior"); continue; } this.Tbehaviors.Add(attribute.Type, behavior); } }
完整的TimeTestComponent 组件代码
using System; using System.Collections.Generic; namespace ETModel { [ObjectSystem] public class TimeTestComponentAwakeSystem : AwakeSystem<TimeTestComponent> { public override void Awake(TimeTestComponent self) { self.Awake(); } } public class TimeTestComponent : Component { private Entity parent; private readonly Dictionary<string, ITimeBehavior> Tbehaviors = new Dictionary<string, ITimeBehavior>(); public void Awake() { this.parent = this.GetParent<Entity>(); this.Load(); } public void Run(string type,long time=0){ try { Tbehaviors[type].Behavior(parent,time); } catch (Exception e) { throw new Exception($"{type} Time Behavior 错误: {e}"); } } //扫描程序集,获取所有具有[TimeBehavior]特性的对象 //都添加到Tbehaviors中 public void Load() { this.Tbehaviors.Clear(); List<Type> types = Game.EventSystem.GetTypes(typeof(TimeBehaviorAttribute)); foreach (Type type in types) { object[] attrs = type.GetCustomAttributes(typeof (TimeBehaviorAttribute), false); if (attrs.Length == 0) { continue; } TimeBehaviorAttribute attribute = attrs[0] as TimeBehaviorAttribute; if (Tbehaviors.ContainsKey(attribute.Type)) { Log.Debug($"已经存在同类Time Behavior: {attribute.Type}"); throw new Exception($"已经存在同类Time Behavior: {attribute.Type}"); } object o = Activator.CreateInstance(type); ITimeBehavior behavior = o as ITimeBehavior; if (behavior == null) { Log.Error($"{o.GetType().FullName} 没有继承 ITimeBehavior"); continue; } this.Tbehaviors.Add(attribute.Type, behavior); } } } }
在Init中添加练习测试组件TimeTestComponent
\Assets\Model\Init.cs
//练习3 TestRoom room = ComponentFactory.Create<TestRoom>(); room.AddComponent<TimeTestComponent>(); room.GetComponent<TimeTestComponent>().Run(Typebehavior.Waiting,5000);
运行unity,就可以看到执行效果
如果只是实现某种等待行为,自然是不需要这么复杂的。下面我们再定义一种时间行为,比如可能用到的,随机时间,随机对房间内玩家进行点名(纯练习演示不能直接实用)
添加一个RandTargetTimeBehavior 时间行为对象
\Assets\Model\Test05\RandTargetTimeBehavior.cs
using ETModel; using System; using System.Threading; using System.Threading.Tasks; namespace ETModel { [TimeBehavior(Typebehavior.RandTarget)] public class RandTargetTimeBehavior : ITimeBehavior { public TestRoom room; public Random rd = new Random(); public int count = 0; public int maxCount; public void Behavior(Entity parent,long time) { room = parent as TestRoom; StartRand(); } private void StartRand(){ room.gamers.Add(1,"gamer01"); room.gamers.Add(2,"gamer02"); room.gamers.Add(3,"gamer03"); room.gamers.Add(4,"gamer04"); room.gamers.Add(5,"gamer05"); maxCount = rd.Next(2,4); //随机点名次数 Log.Info($"将要点名{maxCount}次"); RandTimeAndTarget().Coroutine(); } private async ETVoid RandTimeAndTarget(){ int num = rd.Next(1,5); string target = room.gamers[num]; //随机被点名者 int randtime = rd.Next(3,12); //随机点名间隔 TimerComponent timer = Game.Scene.GetComponent<TimerComponent>(); room.randCts = new CancellationTokenSource(); await timer.WaitAsync((randtime+1)*1000,room.randCts.Token); Log.Info($"{room.GetType().ToString()}-执行间隔{randtime}秒点名{target}"); room.randCts.Dispose(); room.randCts = null; count++; if(count<maxCount){ RandTimeAndTarget().Coroutine(); } } } }
在测试TestRoom中增加randCts和一个只有编号与名字值对的玩家字典
\Assets\Model\Test05\TestRoom.cs
using System.Collections.Generic; using System.Threading; namespace ETModel { public sealed class TestRoom : Entity { public CancellationTokenSource waitCts; public CancellationTokenSource randCts; public Dictionary<int, string> gamers = new Dictionary<int, string>(); } }
在Init中添加练习测试组件TimeTestComponent和随机点名时间行为的调用
\Assets\Model\Init.cs
room.GetComponent<TimeTestComponent>().Run(Typebehavior.RandTarget);
运行unity可以看到测试效果
还可以开发某种任务倒计时的时间行为对象,比如给玩家添加TimeTestComponent,就可以调用任务倒计时行为。
这几节的练习都是为了让大家通过简化的例子来体会实体,组件,系统事件,扩展方法或自定义方法的使用。