Unity 框架学习--1

由浅入深,慢慢演化实现框架

两个类的实现代码完全一样,就只有类名或类型不一样的时候,而且还需要不断扩展(未来会增加各种事件)的时候,这时候就用 泛型 + 继承 来提取,继承解决扩展的问题,泛型解决实现代码一致,类不一致的问题,这是一个重构技巧。

表现和数据要分离

数据在大多数情况下需要在多个场景、界面、游戏物体之间是共享的,这些数据不但需要在空间上共享,还需要再时间上也需要共享(需要存储起来),所以在这里,开发者的共识就是把数据的部分会抽离出来,单独放在一个地方进行维护,而比较常见的开发架构就是使用 MVC 的开发架构,我们先不用 MVC 的开发架构,而只用 MVC 中的其中一个概念,就是 Model。

Model 就是管理数据、存储数据,管理数据就是可以通过 Model 对象或类可以对数据进行增删改查,有的时候还可以进行存储。

public class GameModel
    {
        public static int KillCount = 0;

        public static int Gold = 0;

        public static int Score = 0;

        public static int BestScore = 0;
    }
  • 用泛型 + 继承 提取 Event 工具类
  • 子节点通知父节点也可以用事件(根据情况)
  • 表现和需要共享的数据分离
  • 正确的代码要放在正确的位置

如果是共享的数据就放在 Model 里,如果不是共享的就不需要。

这里共享的数据可以是配置数据、需要存储的数据、多个地方需要访问的数据

对于配置数据来说,游戏中的场景和 GameObject、Prefab 在运行游戏之前也是一种配置数据,因为他们本质上是用 Yaml(类似 json、xml 的数据格式)存储在磁盘上的。

而需要存储的数据是从时间这个维度共享的数据,即现在可以访问以前某个时刻存储的数据。

 

 复用  可绑定属性

using System;

namespace FrameworkDesign
{
    public class BindableProperty<T> where T : IEquatable<T>
    {
        private T mValue;

        public T Value
        {
            get => mValue;
            set
            {
                if (!mValue.Equals(value))
                {
                    mValue = value;
                    OnValueChanged?.Invoke(value);
                }
            }
        }

        public Action<T> OnValueChanged;
    }
}

BidableProperty 是 数据 + 数据变更事件 的合体,它既存储了数据充当 C# 中的 属性这样的角色,也可以让别的地方监听它的数据变更事件,这样会减少大量的样板代码

  • 表现逻辑 适合用 事件 或 委托
  • 表现逻辑用方法调用会造成很多问题,Controller 臃肿难维护、
  • Model 和 View 是自底向上的关系
  • 自底向上用事件或委托
  • 自顶向下用方法调用

命令模式

用一个方法来写的逻辑,改成用对象来实现,而这个对象只有一个执行方法。

我们先定义一个接口,叫做 ICommand,代码如下:

namespace FrameworkDesign
{
    public interface ICommand
    {
        void Execute();
    }
}

实现接口:

public struct AddCountCommand : ICommand
    {
        public void Execute()
        {
            CounterModel.Count.Value++;
        }
    }

Command 模式就是逻辑的调用和执行是分离的

空间分离的方法就是调用的地方和执行的地方放在两个文件里。

时间分离的方法就是调用的之后,Command 过了一点时间才被执行。

而 Command 模式由于有了调用和执行分离这个特点,所以我们可以用不同的数据结构去组织 Command 调用,比如命令队列,再比如用一个命令的堆栈,来实现撤销功能(ctrl + z)

引入单例

  • 静态类没有访问限制。
  • 使用 static 去扩展模块,其模块的识别度不高。
public class Singleton<T> where T : class
    {
        public static T Instance
        {
            get
            {
                if (mInstance == null)
                {
                    // 通过反射获取构造
                    var ctors = typeof(T).GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic);
                    // 获取无参非 public 的构造
                    var ctor = Array.Find(ctors, c => c.GetParameters().Length == 0);

                    if (ctor == null)
                    {
                        throw new Exception("Non-Public Constructor() not found in " + typeof(T));
                    }

                    mInstance = ctor.Invoke(null) as T;
                }

                return mInstance;
            }
        }

        private static T mInstance;
    }

模块化优化--引入 IOC 容器

IOC 容器,大家可以理解为是一个字典,这个字典以 Type 为 key,以对象即 Instance 为 value,非常简单。

而 IOC 容器最少有两个核心的 API,即根据 Type 注册实例,根据 Type 获取实例。

实现一个简单的 IOC 容器

public class IOCContainer
    {
        /// <summary>
        /// 实例
        /// </summary>
        public Dictionary<Type, object> mInstances = new Dictionary<Type, object>();

        /// <summary>
        /// 注册
        /// </summary>
        /// <param name="instance"></param>
        /// <typeparam name="T"></typeparam>
        public void Register<T>(T instance)
        {
            var key = typeof(T);

            if (mInstances.ContainsKey(key))
            {
                mInstances[key] = instance;
            }
            else
            {
                mInstances.Add(key,instance);
            }
        }

        /// <summary>
        /// 获取
        /// </summary>
        public T  Get<T>() where T : class
        {
            var key = typeof(T);
            
            object retObj;
            
            if(mInstances.TryGetValue(key,out retObj))
            {
                return retObj as T;
            }

            return null;
        }
    }

将这个代码用起来:

我们先创建一个 CounterApp 类,用于注册全部模块,代码如下:
using FrameworkDesign;

namespace CounterApp
{
    public class CounterApp
    {
        private static IOCContainer mContainer = null;

        // 确保 Container 是有实例的
        static void MakeSureContainer()
        {
            if (mContainer == null)
            {
                mContainer = new IOCContainer();
                Init();
            }
        }

        // 这里注册模块
        private static void Init()
        {
            mContainer.Register(new CounterModel());
        }
        
        // 提供一个获取模块的 API
        public static T Get<T>() where T : class
        {
            MakeSureContainer();
            return mContainer.Get<T>();
        }
    }
}
接着我们把 CounterApp 类应用起来,代码如下:
using FrameworkDesign;
using UnityEngine;
using UnityEngine.UI;

namespace CounterApp
{
    public class CounterViewController : MonoBehaviour
    {
        private CounterModel mCounterModel;
        
        void Start()
        {
            // 获取
            mCounterModel = CounterApp.Get<CounterModel>();
            
            // 注册
            mCounterModel.Count.OnValueChanged += OnCountChanged;

            transform.Find("BtnAdd").GetComponent<Button>()
                .onClick.AddListener(() =>
                {
                    // 交互逻辑
                    new AddCountCommand()
                        .Execute();
                });

            transform.Find("BtnSub").GetComponent<Button>()
                .onClick.AddListener(() =>
                {
                    // 交互逻辑
                    new SubCountCommand()
                        .Execute();
                });
            
            OnCountChanged(mCounterModel.Count.Value);
        }

        // 表现逻辑
        private void OnCountChanged(int newValue)
        {
            transform.Find("CountText").GetComponent<Text>().text = newValue.ToString();
        }

        private void OnDestroy()
        {
            // 注销
            mCounterModel.Count.OnValueChanged -= OnCountChanged;

            mCounterModel = null;
        }
    }

    /// <summary>
    /// 不需要是单例了
    /// </summary>
    public class CounterModel
    {
        public BindableProperty<int> Count = new BindableProperty<int>()
        {
            Value = 0
        };
    }
}
AddCountCommand.cs
using FrameworkDesign;

namespace CounterApp
{
    public struct AddCountCommand : ICommand
    {
        public void Execute()
        {
            CounterApp.Get<CounterModel>().Count.Value++;
        }
    }
}
SubCountCommand.cs
using FrameworkDesign;

namespace CounterApp
{
    public struct SubCountCommand : ICommand
    {
        public void Execute()
        {
            CounterApp.Get<CounterModel>().Count.Value--;
        }
    }
}

--dd,这就是框架吗  orz

以下代码容易重复

PiontGame.cs
namespace FrameworkDesign.Example
{
    public class PointGame 
    {
        private static IOCContainer mContainer = null;

        // 确保 Container 是有实例的
        static void MakeSureContainer()
        {
            if (mContainer == null)
            {
                mContainer = new IOCContainer();
                Init();
            }
        }

        // 这里注册模块
        private static void Init()
        {
            mContainer.Register(new GameModel());
        }
        
        // 提供一个获取模块的 API
        public static T Get<T>() where T : class
        {
            MakeSureContainer();
            return mContainer.Get<T>();
        }
    }
}

优化一下:创建一个类,名字叫 Architecture.cs ,代码如下:

namespace FrameworkDesign
{
    /// <summary>
    /// 架构
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public abstract class Architecture<T> where T : Architecture<T>, new()
    {
        #region 类似单例模式 但是仅在内部课访问
        private static T mArchitecture = null;
        
        // 确保 Container 是有实例的
        static void MakeSureArchitecture()
        {
            if (mArchitecture == null)
            {
                mArchitecture = new T();
                mArchitecture.Init();
            }
        }
        #endregion

        private IOCContainer mContainer = new IOCContainer();

        // 留给子类注册模块
        protected abstract void Init();

        // 提供一个注册模块的 API
        public void Register<T>(T instance)
        {
            MakeSureArchitecture();
            mArchitecture.mContainer.Register<T>(instance);
        }

        // 提供一个获取模块的 API
        public static T Get<T>() where T : class
        {
            MakeSureArchitecture();
            return mArchitecture.mContainer.Get<T>();
        }
    }
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值