任务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,就可以调用任务倒计时行为。

这几节的练习都是为了让大家通过简化的例子来体会实体,组件,系统事件,扩展方法或自定义方法的使用。

 

posted @ 2023-01-30 18:49  Domefy  阅读(37)  评论(0)    收藏  举报