前言
当我们项目中的代码越来越多时,就会出现一些复杂程度上或是代码逻辑上的问题,这时候就需要用一些固定的解决方案来帮助我们解决问题。这些解决方案不仅可以解决代码框架结构的问题,而且可以很好的降低代码之间的耦合度。这些解决方案就是设计模式。而状态模式就是设计模式中的一种。
一、啥子是状态模式?
当对象内部的状态发生改变时,其实也就是改变了他的行为,让对象看起来好像更改了类一样,这就是状态模式。状态模式在游戏开发中非常常用。
二、注意看!这有个非状态与状态的对比!
我们以一个游戏中的敌人为例,他在游戏中可以分为好几个状态,如发呆,追逐,攻击等,我们可以通过敌人的动作来区分其状态(这里的状态不止可以通过动作来区分,只要能区分出同一个对象处于不同阶段都可以,比如精神上的萎靡不振, 精神满满等)。
以下是两种方式的实现。
非状态模式代码
让我们来瞅瞅非状态模式的代码
using UnityEngine;
//定义一个敌人类
public class Enemy
{
//假定敌人通过和玩家的 distance(距离) 做不同的动作
public void Enemy_Action(float distance)
{
//距离大于 10 时发呆, 距离在 [2,10) 之间追逐, [0, 2]则进行攻击
if (distance >= 10)
{
Debug.Log("我正在执行发呆的一大串代码!");
}
else if (distance < 10 && distance > 2)
{
Debug.Log("我正在执行追逐的一大串代码!");
}
else
{
Debug.Log("我正在执行攻击的一大串代码!");
}
}
}
public class State : MonoBehaviour
{
void Start()
{
Enemy theEnemy_One = new Enemy();
//通过接收不同的距离,敌人会做出不同动作
theEnemy_One.Enemy_Action(1);
theEnemy_One.Enemy_Action(8);
theEnemy_One.Enemy_Action(11);
}
}
(dis 为 distance 缩写)
在上段代码中,敌人根据和玩家的距离来做出不同动作,如果该敌人的动作比较多样化,比如 dis=15 时用手枪攻击,dis=20时用步枪,dis=30用火箭筒,40时用导弹!那么这时候必定会出现大量 if 语句和实现动作的代码,**当这些代码都包含在一个方法中时,你就会发现这个方法特别臃肿!一眼望去会显得十分混乱。
思考1:上述情况的思维方式是面向对象还是面向过程?
状态模式代码
为了避免上述情况, 我们的状态模式就来了!
using UnityEngine;
//定义一个抽象状态类,作为每个状态的父类
public abstract class TheState
{
//定义一个每个状态都要实现的抽象方法,具体逻辑在每个状态中由状态自己实现!
public abstract void Do();
}
//定义发呆状态类,继承自抽象父类,并重写方法,方法中编写发呆逻辑!
public class Idle : TheState
{
public override void Do()
{
Debug.Log("我正在执行发呆的一大串代码!");
}
}
//定义追逐状态类,继承自抽象父类,并重写方法,方法中编写追逐逻辑!
public class Chase : TheState
{
public override void Do()
{
Debug.Log("我正在执行追逐的一大串代码!");
}
}
//定义攻击状态类,继承自抽象父类,并重写方法,方法中编写攻击逻辑!
public class Attack : TheState
{
public override void Do()
{
Debug.Log("我正在执行攻击的一大串代码!");
}
}
//定义敌人类
public class Enemy
{
//每个敌人包含自己当前的状态
public TheState theState;
//根据离玩家的距离切换状态
public void Enemy_Action(float distance)
{
if (distance >= 10)
{
theState = new Idle();
}
else if (distance < 10 && distance > 2)
{
theState = new Chase();
}
else
{
theState = new Attack();
}
//通过上面代码切换完状态后,调用状态的方法做具体的动作。
theState.Do();
}
}
public class State : MonoBehaviour
{
void Start()
{
Enemy enemy = new Enemy();
enemy.Enemy_Action(1);
enemy.Enemy_Action(8);
enemy.Enemy_Action(11);
}
}
对比两种方式下的 Enemy类,就会发现非状态模式下敌人的动作代码是直接在该类中实现,而状态模式则是将其放在了对应的类中,这不但能让代码逻辑变得更加清晰,还能提高复用性,试想一下,假如有一个敌人脚本的代码特别多,逻辑特别复杂,在这种情况下,你是愿意继续往复杂的代码里添加功能呢还是愿意从其他类获取功能?
三、如何使用状态模式
使用状态模式的大概思路为下:
1:创建一个状态的基类 (上文中的 TheState 类)
2:为所有状态分别创建一个状态类并继承基类,每个状态类中完善自己的逻辑 (上文中的 Idle,Chase,Attack 三个类)
3:修改代码,在对应条件下修改为对应状态(上文中在 Enemy类 中根据距离远近就修改状态)
总结
合理的使用状态模式,不只会让代码逻辑更加清晰,还能提高代码的复用性!(本篇文章若有错误,欢迎指正!)