Aprendizaje del marco de Unity--1

De superficial a profundo, evolucione lentamente el marco de implementación

Los códigos de implementación de las dos clases son exactamente los mismos, solo cuando los nombres o tipos de clase son diferentes y cuando necesitan expandirse continuamente (se agregarán varios eventos en el futuro), luego use genéricos + herencia para extraer, y la herencia resuelve el problema de la expansión. Los genéricos resuelven el problema del código de implementación consistente y las clases inconsistentes . Esta es una técnica de refactorización.

El rendimiento y los datos deben separarse

En la mayoría de los casos, los datos deben compartirse entre múltiples escenas, interfaces y objetos del juego. Estos datos no solo deben compartirse en el espacio, sino que también deben compartirse en el tiempo (deben almacenarse), así que aquí, desarrollo El consenso entre los lectores es extraer la parte de datos y colocarla en un lugar separado para su mantenimiento. La arquitectura de desarrollo más común es usar la arquitectura de desarrollo MVC. No usaremos la arquitectura de desarrollo MVC primero, solo usaremos una de las MVC. El concepto es modelo.

El modelo es administrar datos y almacenar datos. Administrar datos significa que los datos se pueden agregar, eliminar, modificar y verificar a través de objetos o clases del modelo y, a veces, también se pueden almacenar.

public class GameModel
    {
        public static int KillCount = 0;

        public static int Gold = 0;

        public static int Score = 0;

        public static int BestScore = 0;
    }
  • Extraiga la clase de herramienta Event usando genéricos + herencia
  • Los nodos secundarios también pueden usar eventos para notificar a los nodos principales (según la situación)
  • Separación de presentación y datos que deben compartirse.
  • El código correcto debe colocarse en la ubicación correcta.

Si los datos se comparten , póngalos en el Modelo, si no se comparten, no es necesario.

Los datos compartidos aquí pueden ser datos de configuración, datos que deben almacenarse y datos a los que se debe acceder en varios lugares .

En cuanto a los datos de configuración, las escenas, GameObjects y Prefabs en el juego también son un tipo de datos de configuración antes de ejecutar el juego, porque esencialmente se almacenan en el disco usando Yaml (formato de datos similar a json y xml).

Los datos que deben almacenarse son datos compartidos desde la dimensión temporal, es decir, ahora se puede acceder a los datos almacenados en un momento determinado del pasado.

 

 Propiedades vinculables reutilizables

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 es una combinación de datos + eventos de cambio de datos. No solo almacena datos y actúa como una propiedad en C#, sino que también permite que otros lugares monitoreen sus eventos de cambio de datos, lo que reducirá una gran cantidad de código repetitivo.

  • La lógica de rendimiento es adecuada para usar eventos o delegados.
  • El uso de llamadas a métodos para la lógica de presentación causará muchos problemas: el controlador está inflado y es difícil de mantener.
  • Modelo y Vista tienen una relación de abajo hacia arriba
  • De abajo hacia arriba usando eventos o delegados
  • Llamada al método de arriba hacia abajo

modo comando

La lógica escrita en un método se implementa mediante un objeto, y este objeto tiene solo un método de ejecución.

Primero definimos una interfaz llamada ICommand, el código es el siguiente:

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

Implementar interfaz:

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

El modo de comando significa que la invocación lógica y la ejecución están separadas.

El método de separación de espacios consiste en colocar el lugar de llamada y el lugar de ejecución en dos archivos.

El método de separación de tiempo es que después de llamarlo, el comando se ejecutará después de un tiempo.

Dado que el modo Comando tiene la característica de separar la invocación y la ejecución, podemos usar diferentes estructuras de datos para organizar las invocaciones de Comando, como colas de comandos, o usar una pila de comandos para implementar la función de deshacer (ctrl + z).

Introducir singleton

  • Las clases estáticas no tienen restricciones de acceso.
  • Utilice estática para ampliar módulos y el reconocimiento del módulo no será alto.
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;
    }

Optimización modular: introducción de contenedores IOC

El contenedor IOC puede entenderse como un diccionario. Este diccionario utiliza el tipo como clave y el objeto, es decir, instancia, como valor. Es muy simple.

El contenedor IOC tiene al menos dos API principales, a saber, registrar una instancia según el tipo y obtener una instancia según el tipo.

Implementar un contenedor COI simple

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;
        }
    }

Utilice este código:

我们先创建一个 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, ¿es este el marco orz?

El siguiente código es fácil de repetir.

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>();
        }
    }
}

Optimízalo: crea una clase llamada Architecture.cs con el siguiente código:

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>();
        }
    }
}

Supongo que te gusta

Origin blog.csdn.net/zaizai1007/article/details/132253776
Recomendado
Clasificación