unity面向组合开发二:EC的代码实践

一、ECCore

需要在Unity项目中使用插件:UniRx,通过UniRx代替Mono的Update,Mono下做轮询性能消耗会有点大。

EntityMono代码:

using System;
using System.Collections.Generic;
using EC;
using UniRx;
using UnityEngine;

namespace ECCore
{
    public abstract class EntityMono : MonoBehaviour
    {
        private readonly List<CapBase> caps = new();
        private IDisposable updateSubscription;
        public CapBase AddCap<T>() where T : CapBase, new()
        {
            // 检查是否已经添加了相同类型的 CapBase 实例
            if (caps.Exists(cap => cap is T))
            {
                Debug.LogWarning($"{typeof(T).Name} 已经添加了!");
                return null;
            }
            // 创建新的实例并添加到 caps 列表中
            var c = (T)System.Activator.CreateInstance(typeof(T));
            caps.Add(c);
            LogErrorByCap(c);
            return c;
        }
        public T GetCap<T>() where T : CapBase
        {
            return (T)caps.Find(cap => cap is T);
        }
        public void RemoveCap(CapBase cap)
        {
            caps.Remove(cap);
        }

        private void Awake()
        {
            OnInit();
            updateSubscription?.Dispose();
            updateSubscription = Observable.Timer(TimeSpan.Zero, TimeSpan.FromSeconds(0.01f))
                .Subscribe(_ => OnUpdate());
            
        }

        private void OnDestroy()
        {
            updateSubscription?.Dispose();
            OnClear();
        }
        // entity生命周期
        protected abstract void OnInit();
        protected abstract void OnUpdate();
        protected abstract void OnClear();
        void OnCollisionEnter(Collision collision)
        {
            GetCap<OnColliderCapBase>()?.OnCollisionEnter(collision);
        }
        void OnCollisionStay(Collision collision)
        {
            GetCap<OnColliderCapBase>()?.OnCollisionStay(collision);
        }
        void OnCollisionExit(Collision collision)
        {
            GetCap<OnColliderCapBase>()?.OnCollisionExit(collision);
        }
        void OnTriggerEnter(Collider otherCollision)
        {
            GetCap<OnTriggerCapBase>()?.OnTriggerEnter(otherCollision);
        }
        void OnTriggerStay(Collider otherCollision)
        {
            GetCap<OnTriggerCapBase>()?.OnTriggerStay(otherCollision);
        }
        void OnTriggerExit(Collider otherCollision)
        {
            GetCap<OnTriggerCapBase>()?.OnTriggerExit(otherCollision);
        }

        private void LogErrorByCap(CapBase cap)
        {
            switch (cap)
            {
                case OnColliderCapBase onColliderCapBase:
                    if (GetComponent<Collider>() == null)
                    {
                        Debug.LogError("碰撞能力实体没有添加Collider:"+ name);
                    }
                    break;
                case OnTriggerCapBase onTriggerCapBase:
                    var colliderComponent = GetComponent<Collider>();
                    if (colliderComponent == null)
                        Debug.LogError("触发能力实体没有添加Collider:"+ name);
                    else
                    {
                        if(!colliderComponent.isTrigger)
                            Debug.LogError("触发能力实体没有勾选isTrigger:"+ name);
                    }
                    break;
            }
        }
    }
}

CapBase代码:

namespace ECCore
{
    public abstract class CapBase
    {
        public abstract void InitData(EntityMono initEntity);
        public abstract void OnClear();
    }
}

二、OnColliderCapBase和OnTriggerCapBase 碰撞能力基类和触发能力基类

碰撞和触发需要依赖Mono下内部方法,所以给碰撞能力和触发能力抽象出基类方法。

OnColliderCapBase代码:

using ECCore;
using UnityEngine;

namespace EC
{
    public abstract class OnColliderCapBase : CapBase
    {
        public abstract void OnCollisionEnter(Collision collision);
        public abstract void OnCollisionStay(Collision collision);
        public abstract void OnCollisionExit(Collision collision);
    }
}

OnTriggerCapBase代码:

using ECCore;
using UnityEngine;

namespace EC
{
    public abstract class OnTriggerCapBase : CapBase
    {
        public abstract void OnTriggerEnter(Collider otherCollision);
        public abstract void OnTriggerStay(Collider otherCollision);
        public abstract void OnTriggerExit(Collider otherCollision);
    }
}

 三、GameEC

PlayerEntity代码:

using ECCore;
using UnityEngine;

namespace EC
{
    public class PlayerEntity : EntityMono
    {
        public float mouseSensitivity = 10.0f;
        protected override void OnInit()
        {
            // AddCap<InputCap>().InitData(this);
            AddCap<MoveCap>().InitData(this);
            AddCap<GravityCap>().InitData(this);
            GetCap<GravityCap>()?.SetGravity(10);
            AddCap<LookCap>().InitData(this);
            AddCap<PlayerOnColliderCap>().InitData(this);
        }

        protected override void OnUpdate()
        {
            // 输入先这么做 不知道要不要加Cap 思考中.....
            Vector3 movement = Vector3.zero;
            Vector2 look = Vector2.zero;
            movement.x = Input.GetAxis("Horizontal");
            movement.z = Input.GetAxis("Vertical");
            look.x = Input.GetAxis("Mouse X");
            look.y = Input.GetAxis("Mouse Y");
            
            GetCap<LookCap>()?.Look(look, mouseSensitivity);
            GetCap<MoveCap>()?.Move(movement);
        }

        protected override void OnClear()
        {
            
        }
    }

}

MoveCap代码:

using UnityEngine;
using ECCore;

namespace EC
{
    public class MoveCap : CapBase
    {
        private EntityMono entity;
        public override void InitData(EntityMono initEntity)
        {
            entity = initEntity;
        }

        public void Move(Vector3 pos)
        {
            pos = entity.transform.TransformDirection(pos);
            entity.transform.position += pos;
        }
        public override void OnClear()
        {
            entity = null;
        }
    }
}
posted @ 2025-10-03 17:20  apssic  阅读(10)  评论(0)    收藏  举报