Unity 设计模式-状态模式(State Pattern)详解
状态模式(State Pattern)
状态模式(State Pattern) 是一种行为型设计模式,它允许一个对象在其内部状态发生改变时改变其行为。状态模式将与状态相关的行为封装在独立的状态类中,系统在运行时根据状态的变化来切换不同的行为。
通过状态模式,状态转换和行为执行得到了很好的分离,符合面向对象设计的单一职责原则,即每个类只负责一项具体的职责。
状态模式的结构
- 上下文类(Context Class):维护当前状态的引用,负责在运行时根据状态的变化调用不同的状态类的行为。
- 状态接口(State Interface):定义状态类的共同行为,这通常是一个抽象类或接口。
- 具体状态类(Concrete State Class):实现状态接口,提供每个状态下具体的行为。
1、什么时候使用状态模式
- 对象的行为依赖于状态变化时,并且行为会随着状态的不同而发生变化。
- 对象的状态变化频繁,而且状态之间的切换逻辑复杂,状态模式可以很好地管理这些状态。
- 状态转换具有规律性,每个状态都有固定的行为模式或规则,状态模式可以简化这种逻辑。
- 需要避免使用大量条件判断来实现不同状态下的行为时,状态模式是一个更优雅的解决方案。
2、使用状态模式的好处
- 简化条件判断逻辑:通过将状态转换逻辑封装到各个具体状态类中,状态模式避免了大量的 if-else 或 switch 语句,代码更清晰。
- 增强系统的扩展性:状态模式将状态相关的行为封装到独立的类中,因此添加新的状态或修改现有状态的行为不会影响其他代码,符合开闭原则。
- 使状态行为与上下文解耦:上下文类仅负责状态的切换,而具体的行为由状态类来实现,保持了上下文类的简洁。
- 提高代码可维护性:状态类各自独立,便于测试、调试和维护。
3、使用状态模式时的注意事项
- 状态类的数量增加:状态模式的一个潜在问题是状态类的数量可能会随着状态的增多而增加,导致类过多。此时需要评估是否有必要使用状态模式,还是可以通过其他方式优化。
- 上下文和状态类的依赖:虽然状态模式将状态行为独立出来,但状态类与上下文之间的相互引用可能导致依赖复杂性,需要控制好状态类的职责。
- 状态转换规则复杂时:如果状态之间的转换规则非常复杂,可能需要将转换逻辑从具体状态类中抽离,避免具体状态类中的代码变得过于庞大。
在 Unity 中使用 状态模式
在 Unity 中,状态模式可以很好地用于角色的状态管理,例如角色的运动状态、攻击状态、死亡状态等。我们可以通过状态模式将这些行为封装在不同的状态类中,使得角色能够根据当前状态动态切换行为。
-
下面我们使用状态模式,实现一个角色可以在站立、行走、奔跑和跳跃状态之间切换的示例
参考类图如下:
1、定义状态接口
我们首先定义一个接口 ICharacterState
,每个具体的状态类都需要实现该接口。
public interface ICharacterState
{
void HandleInput(Character character);
void UpdateState(Character character);
}
2、具体状态类
根据不同的角色状态(站立、行走、奔跑、跳跃),我们为每个状态创建一个具体的类。每个状态类都实现了 ICharacterState
接口。
using UnityEngine;
// 站立状态类
public class StandState : ICharacterState
{
public void HandleInput(Character character)
{
if (Input.GetKeyDown(KeyCode.W))
{
character.SetState(new WalkState());
}
}
public void UpdateState(Character character)
{
Debug.Log("角色正在站立");
}
}
// 行走状态类
public class WalkState : ICharacterState
{
public void HandleInput(Character character)
{
if (Input.GetKeyDown(KeyCode.R))
{
character.SetState(new RunState());
}
else if (Input.GetKeyDown(KeyCode.Space))
{
character.SetState(new JumpState());
}
}
public void UpdateState(Character character)
{
character.transform.Translate(Vector3.forward * 2f * Time.deltaTime);
Debug.Log("角色正在行走");
}
}
// 奔跑状态类
public class RunState : ICharacterState
{
public void HandleInput(Character character)
{
if (Input.GetKeyDown(KeyCode.Space))
{
character.SetState(new JumpState());
}
}
public void UpdateState(Character character)
{
character.transform.Translate(Vector3.forward * 5f * Time.deltaTime);
Debug.Log("角色正在奔跑");
}
}
// 跳跃状态类
public class JumpState : ICharacterState
{
public void HandleInput(Character character)
{
// 空中不允许其他输入
}
public void UpdateState(Character character)
{
character.transform.Translate(Vector3.up * 5f * Time.deltaTime);
Debug.Log("角色正在跳跃");
// 跳跃结束后返回站立状态
character.SetState(new StandState());
}
}
3、上下文类
接下来定义上下文类 Character
,它维护当前的状态,并通过 SetState()
方法进行状态的切换。
using UnityEngine;
public class Character : MonoBehaviour
{
private ICharacterState currentState;
void Start()
{
// 初始化状态为站立
currentState = new StandState();
}
void Update()
{
// 处理输入并更新状态
currentState.HandleInput(this);
currentState.UpdateState(this);
}
// 切换状态
public void SetState(ICharacterState newState)
{
currentState = newState;
}
}
4、使用场景中的角色
在 Unity 场景中,我们可以将此状态系统应用到一个角色对象上,比如一个 3D 模型。使用状态模式,角色将根据输入切换行为,比如从站立到行走,再到奔跑或跳跃。
创建一个新的脚本 GameController
来管理和初始化角色。
using UnityEngine;
public class GameController : MonoBehaviour
{
public GameObject characterPrefab;
private Character character;
void Start()
{
// 初始化角色对象
GameObject characterObject = Instantiate(characterPrefab);
character = characterObject.AddComponent<Character>();
}
}
5、在 Unity 中运行
创建一个空的 Unity 场景,放置一个 3D 角色(立方体、模型等)作为 characterPrefab。
添加 Character 和 GameController 脚本到 Unity 场景。
在游戏运行时,角色会根据键盘输入(W 切换到行走状态,R 切换到奔跑状态,空格键切换到跳跃状态)来切换不同的行为。
6、示例分析
- 初始状态为站立:当游戏开始时,角色默认处于站立状态。此时控制台输出“角色正在站立”,并且没有移动。
- 切换到行走状态:当玩家按下 W 键时,角色进入行走状态,角色开始向前移动,控制台输出“角色正在行走”。
- 切换到奔跑状态:按下 R 键,角色从行走状态切换到奔跑状态,移动速度加快,控制台输出“角色正在奔跑”。
- 切换到跳跃状态:按下空格键,角色进入跳跃状态,角色向上移动,跳跃结束后角色自动返回站立状态。
状态模式在 Unity 中非常适用于处理角色的状态转换。例如,角色的运动状态、游戏中的敌人 AI 状态等。通过将每个状态的行为封装成独立的类,我们可以灵活地进行状态切换并保持代码结构的清晰与可扩展性。
- 简化了复杂的状态管理:避免使用大量的 if-else 或 switch-case 语句,减少了冗余代码。
- 高可扩展性:当需要添加新的状态时,只需要新建一个状态类即可,不会影响现有的代码。
- 增强了代码的可维护性:各个状态之间相互独立,符合单一职责原则,每个状态类只负责处理特定状态下的行为。
今天是2024年11月25日
重复一段毒鸡汤来勉励我和你
你的对手在看书
你的仇人在磨刀
你的闺蜜在减肥
隔壁的老王在练腰
而你在干嘛?