每日一句:"少年一贯快马扬帆 道阻且长不转弯 要盛大要绚烂要哗然 要用理想的泰坦尼克去撞现实的冰川 要当烧赤壁的风而非借箭的草船 要为一片海就肯翻万山。"
目录
状态模式:
外观模式
组合模式,
单例模式
命令模式
观察者模式
工厂模式
对象池模式
GC垃圾回收
状态模式:
当对象内部的状态发生变化时,其实也就是改变了它的行为,让对象看起来好像更改了类一样,这就是状态模式。
状态模式的代码实现:
示例:学生的日常生活
实现路径:创建一个状态的基类——>为睡觉、娱乐和学习分别创建一个状态类并继承状态基类——>修改代码,在不同的时间段加载不同的状态。
//抽象状态类,这里作为每个状态的父类
public abstract void class State
{//每个状态都要实现的抽象方法public abstarct void Run();
}
//睡觉状态
public class SleepState:State
{ public override void Run()
{Debug.Log(“睡觉状态的执行代码”);}}
//娱乐状态
public class PlayState:State
{ public override void Run()
{Debug.Log(“娱乐状态的执行代码”);}}
//学习状态
public class StudyState:State
{ public override void Run()
{Debug.Log(“学习状态的执行代码”);}}
//学生类
public class NewStudent
{ //每个学生都包含一个当前的状态
public State state;
//接受时间,并切换学生该时间段的状态
public void Run(int time)
{ if(time>22||time<7)
state=new SleepState();
else if(time>=7&&time<=18)
state=new SleepState();
else
state=new PlayState();
//调用改状态
state.Run();
}
public class StateTest:MonoBehaviour
{ void Start(){
NewStudent student=new NewStudent ();
//做18点的事情
student.Run(18);
//做10点的事情
student.Run(10);
}
}
状态模式还有一个好处,那就是可以很方便地进行状态的复用,例如这时再加一个工人类,可以很方便地让这2个类使用同一个睡觉状态,所以当代码量越多,逻辑越复杂时更能体现状态模式的好处。
外观模式
为子系统提供一组统一的高层接口,使子系统更加容易使用,这就是外观模式。
外观模式可以将代码的复杂性封装起来并对外提供一个访问接口,让编程人员在使用时仅仅需要调用访问接口,而不需要关心内部复杂代码的实现和功能。
组合模式,
脚本之间的通讯
var xx=GetComponent<Walk>();
xx.Walk();
GetCompoenet()的性能问题
- 避免在Update中来使用GetComponent,否则性能代价过于昂贵
- 若多处地方使用GetComponent,尝试初始化获得组件的引用避免反复获取
单例模式
保证一个类只有一个实例,且具有全局访问点
public static Singleton Instance{get;private set;}
//将实例进行静态化static
void Awake()
{ //将实例进行静态化static
if(Instance!=null&&Instance!=this)
{Destroy(this;}
else
{Instance=this;}
}
优点:
单例模式只会在第一次请求时被创建,不会自主创建,节约内存
只存在一个对象进行运行,不用经历对象创建和销毁,节省性能
可以轻松的链接游戏各个模块
缺点:
随着项目增大,代码耦合度上升,维护困难
命令模式
将请求封装为一个对象,从而使你可用不同的请求对客户进行参数化对请求排队或记录请求日志,以及支持可撤销的操作
观察者模式
定义了一个一对多的依赖关系,让多个观察者监听同一个主体对象,当主体对象发生变化时,会通知所有的观察者,使观察者可以自己进行更新。
public Action<> 人物死亡时事件;
if(Hp==0)
{人物死亡时事件.执行();}
代码层面,各管各的
执行层面,协同合作
工厂模式
定义一个创建对象的接口,让子类决定实例化哪一类,让类的实例化延迟到子类中进行,这就是工厂模式。
工厂模式提供了一种创建多种类型的对象的方式。试想一下,如果有不同品牌的汽车,那么购买汽车的人并不会关心汽车的生产过程,购买人只需要提供购买汽车的型号,就会得到对应型号的汽车,这个其实就是工厂模式。
实例:
实现路径:创建一个汽车抽象类—>创建多个汽车类并继承汽车抽象类—>创建一个汽车工厂类—>通过工厂类进行汽车的实例化
//汽车抽象类
public abstract class Car
{ public abstract void Run();}public class Bmw:Car
{ public override void Run()
{ Debug.Log(“宝马”);
{
}public class Benz:Car
{ public override void Run()
{ Debug.Log(“奔驰”);
{
}public class Audi:Car
{ public override void Run()
{ Debug.Log(“奥迪”);
{
}//汽车类型
public enum CarType
{ Bmw,Benz,
Audi
}
//工厂类
public class Factory
{//创建汽车对象的方法,这里将3种汽车的实例化方法封装起来
public static Car Create(CarType type)
{Car car=null;
switch(type)
{case CarType.Bmw:
car=new Bmw();
break;
case CarType.Benz:
car=new Benz();
break;
case CarType.Audi:
car=new Audi();
break;
}
return car;
}
}
public class FactoryTest:MonoBehaviour
{ void Start(){//创建3种不同类型的汽车,这里看到实例化方法被封装起来了
//所以调用我们自己的创建方法就可以了,不必在意内部的创建过程
Car bmw=Factory.Create(CarType.Bmw);
bmw.Run();
Car benz=Factory.Create(CarType.Benz);
benz.Run();
Car audi=Factory.Create(CarType.Audi);
audi.Run();
}
}
在创建对象时,由于类的实例化过程已经被隐藏了起来,因此使用了统一的创建方法。这样当类型数量过多的时候,使用起来会十分方便,并且具有很好的扩展性,可以随时对封装的类进行修改或添加。
对象池模式
GC垃圾回收
值类型
当局部变量超出作用域时,会自动将那些产生的内存进行释放(性能开销小)
在执行垃圾回收时会检查整个堆,因此会造成卡顿
加载界面可以手动进行回收,System.GC.Collect()
如何避免产生常见的Unity垃圾?
不断在堆中分配内存,产生数组
void Update()
{Physics.RaycastAll(new Ray());}
协程返回会创建一个引用类型数据
IEnumerator Wait()
{yield return new WaitForSeconds(s);}
所以如果有多个协程存在着相同的等待时间,
可以将它提取出来进行赋值
实例化对象产生大量垃圾
所以用对象池实例化
调试日志产生大量垃圾——>Debug.Log(“”);
所以构建发布游戏之前关闭日志消息功能
只要不在每一帧的函数里,持续创造大量垃圾就行