LOADING

加载过慢请开启缓存 浏览器默认开启

设计模式、对象池、ECS

设计模式、对象池、ECS

设计模式

更好的单例模式

我们可能会用到多个单例类,对于每个单例类都写上那么一段单例模式代码实在不是很优雅。 我们可以创建一个通用的单例类,并让每个真正用到的单例类继承自它就好了!

public class Singleton<T> where T : class, new()
{
 	protected Singleton() {}
	 private static T _inst = null;
 	public static T instance => _inst ?? (_inst = new T());
    //??运算符:a??b是一种简写方法,等价于a!=null?a:b,即a不为null时返回a,否则返回b
    //可以直接使用lambda表达式来创建getter
 	public static void Clear()
	{
	 	_inst = null;
 	}
}

缺点:有时一个全局的访问点可能并不是好事,因为这样会增加类之间的耦合度,降低可读性,提高出错率。其实很多时候我们可以使用static字段与方法来代替单例;当我们过度使用单例时,每当一个单例类出错,很可能会导致整个系统崩溃。

指令模式 Command Pattern

将我们的每一个操作抽象成一个指令,并给每个指令一个“撤销”的操作。

『解耦』、『撤销功能』

public interface ICommand//公共的指令接口
{
	void Execute();
	void Undo();
}
public class ConcreteCommand : ICommand
    //每一个实际的指令都是一个继承自Icommand接口的类。执行该指令时调用Execute方法,撤销时调用Undo方法。
{
	public void Execute()//执行指令
    {
	···
	}
	public void Undo()//撤销指令
    {
	···
 	}
}

  • (1)指令接口(Command Interface)
  • (2)指令类(Concrete Command)
  • (3)调用者(Invoker):用来存储、调用指令,类似一个“Manager”
  • (4)接收者(Receiver):用来真正实现一个指令
  • (5)使用者(Client):我们在这里创建指令并执行它们

工厂模式 Factory Pattern

public abstract class Animal
{
	public abstract string Speak();
}
public class Dog : Animal
{
    public override string Speak()
    {
    return "Woof";
	}
}
public class Cat : Animal
{
    public override string Speak()
    {
    return "Meow";
    }
}
//工厂类
public static class AnimalFactory
{
     public static Animal CreateAnimal(string type)
     {
         switch (type)
         {
             case "Dog":
             return new Dog();
             case "Cat":
             return new Cat();
             default:
             throw new NotSupportedException();
         }
     }
}
//将工厂继续抽象,得到一个生成工厂的接口,原本的工厂只能生产单一的物品,比如我要生成黑猫和白猫的话就很麻烦
public interface IAnimalFactory//这样可以得到多个工厂
{
     Dog CreateDog();
     Cat CreateCat();
}
public class AnimalFactory : IAnimalFactory//真正的工厂类继承自工厂接口
{
    public Dog CreateDog()
    {
    return new Dog();
	}
 
	public Cat CreateCat()
    {
	return new Cat();
	}
}

对象池 Object Pool

是为了优化性能,如果一个GO频繁被实例化和Destroy掉,会产生大量的GC垃圾,而如果把本该销毁的GO改为enable,将其移动到屏幕之外,到再次需要启用时候,仅仅只进行坐标变换,那么消耗的性能会大大减少。

下面是一个简单的对象池

public class ObjectPool : MonoBehaviour
{
    public GameObject objectToPool; 
    public int initialPoolSize = 10; 
    private List<GameObject> pool;
    void Start()
    {
        pool = new List<GameObject>();
        for (int i = 0; i < initialPoolSize; i++)
        {
        GameObject obj = Instantiate(objectToPool);
        obj.SetActive(false);
        pool.Add(obj);
        }
    }
    public GameObject Get()
    {
        for (int i = 0; i < pool.Count; i++)
        {
            if (!pool[i].activeInHierarchy)
            {
            return pool[i];
            }
        }
        GameObject obj = Instantiate(objectToPool);
        obj.SetActive(false);
        pool.Add(obj);
        return obj;
    }
    public void ReturnToPool(GameObject obj)
    {
   		obj.SetActive(false);
    }
}

ECS(Entity、Component、System)模式

这是一种面向数据的编程范式,可以获得更高的内存效能和更好的代码并行性。

Entity 实体 和GameObject类似,entity是一个有ID的物体,其中有且只有一系列的component。

Component组件和Unity中的component不同,ECS中的component只不过是一些数据结构。它们提供了物体执行逻辑时所需要的数据。 使用Unity中的component时,我们一般不会使用它所以得功能,所以造成了性能浪费。

System系统System是真正完成物体逻辑的部分。一个system会在所有有特定component的entity上执行操作。(比如一个Render System可能会在所有有sprite renderer组件的物体上执行渲染操作)

//eg.
class Position 
{
    public float x;
    public float y;
}
class Velocity 
{
    public float dx;
    public float dy;
}
class Entity 
{
    public string id;
    public Position position;
    public Velocity velocity;
}
class MovementSystem 
{
    public void Update(Entity entity, float deltaTime) 
    {
        if(entity.position != null && entity.velocity != null)
        {
            entity.position.x += entity.velocity.dx * deltaTime;
            entity.position.y += entity.velocity.dy * deltaTime;
        }
    }
}