1、模式标准
模式名称:状态模式
模式分类:行为型
模式意图:允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。
结构图:
适用于:
1、一个对象的行为决定于它的状态,并且它必须在运行时刻根据状态改变它的行为。
2、一个操作中含有庞大的多分支的条件语句,且这些分支依赖丁该对象的状态。这个状态常用一个或多个枚举常量表示。通常,有多个操作包含这一相同的条件结构。State模式将每一个条件分支放入一个独立的类中。这使得开发者可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象独立变化。
主要成员:
- 上下文(Context):它定义了客户端感兴趣的接口,并且维护一个指向当前状态的实例变量。
- 状态抽象(State):这是一个接口或者抽象类,它定义了每个状态必须实现的方法。
- 具体状态(Concrete States):它们是实现状态接口的类,每个类对应一种状态,且包含了该状态下的行为实现。
2、分析与设计
在一般的游戏开发中状态值通常是一个枚举值,但在状态模式中,状态值是一个通过实现了 IUnitState
接口的对象表示的。这种方法的优点是它更加灵活和强大,因为这个状态值不仅仅是一个值,它还是一组行为的集合(即方法实现)。这允许您在不同的状态之间切换行为,而不是仅仅改变一个表示状态的值。
在游戏中的单位一般有以下几种状态:站立,移动,攻击,释放技能中,眩晕中,死亡。比较常见的是单位正在释放一个技能,这个时候一个飞锤飞过来,将他击晕了,他停止了技能的释放。
接下来我们修改一下我们的意图
意图:允许一个对象(单位)在其内部状态改变时(由其状态对象来)改变它的行为。对象看起来似乎修改了它的类(实际是状态对象干的)。
3、开始打造
export enum UnitStateType {
Standing,
Moving,
Attacking,
CastSkilling,
Stuning,
Die
}
export interface IUnitState {
enterState(unitItem: IUnitItem): void
//
stand(): void; // 站立
move(): void; // 移动
attack(): void; // 攻击
castSkill(): void; // 释放技能
stun(): void; // 击晕
die(): void; // 死亡
//
getType(): UnitStateType
}
// 状态基类,包含一个指向Unit的引用
export abstract class BaseState implements IUnitState {
protected unitItem: IUnitItem;
enterState(unitItem: IUnitItem) {
this.unitItem = unitItem;
}
// 获取状态的type值
abstract getType(): UnitStateType;
// 状态
stand() {
console.log(this.unitItem, "单位准备进入站立状态");
this.unitItem.setState(new StandingState());
}
move() {
console.log(this.unitItem, "单位准备进入移动状态");
this.unitItem.setState(new MovingState());
}
attack(): void {
console.log(this.unitItem, "单位准备进入攻击状态");
this.unitItem.setState(new AttackingState());
}
castSkill(): void {
console.log(this.unitItem, "单位准备进入释放技能状态");
this.unitItem.setState(new CastSkillingState());
}
stun(): void {
console.log(this.unitItem, "单位准备进入击晕状态");
this.unitItem.setState(new StuningState());
}
die() {
console.log(this.unitItem, "单位准备进入死亡状态");
this.unitItem.setState(new DeadState());
}
}
// 站立状态
export class StandingState extends BaseState {
getType(): UnitStateType {
return UnitStateType.Standing;
}
// 重写方法
stand() {
console.log(this.unitItem, "单位已经进入站立状态");
}
}
// 移动状态
export class MovingState extends BaseState {
getType(): UnitStateType {
return UnitStateType.Moving;
}
// 重写方法
move() {
console.log(this.unitItem, "单位已经进入移动状态");
}
}
// 攻击状态
export class AttackingState extends BaseState {
getType(): UnitStateType {
return UnitStateType.Attacking;
}
enterState(unitItem: IUnitItem) {
super.enterState(unitItem);
this.doAction();
}
doAction() {
// 执行攻击
this.unitItem.role.attack(); // 攻击
// 如果攻击顺利完成,进行清理并返回到正常状态
// 例如,设置一个延时来模拟攻击动作的时间
let attackDuration = 1000
setTimeout(() => {
if (this.unitItem.getCurrentState().getType() == UnitStateType.Attacking) {
console.log('单位已从攻击状态到站立状态')
this.unitItem.setState(new StandingState());
}
}, attackDuration);
}
// 重写方法
attack(): void {
console.log(this.unitItem, "单位已经进入攻击状态");
}
}
// 释放技能状态
export class CastSkillingState extends BaseState {
getType(): UnitStateType {
return UnitStateType.CastSkilling;
}
enterState(unitItem: IUnitItem) {
super.enterState(unitItem);
this.doAction();
}
doAction() {
// 执行攻击
// this.unitItem.role.attack(); // 攻击
// 如果攻击顺利完成,进行清理并返回到正常状态
// 例如,设置一个延时来模拟攻击动作的时间
let attackDuration = 1000
setTimeout(() => {
if (this.unitItem.getCurrentState().getType() == UnitStateType.CastSkilling) {
console.log('单位已从技能释放状态到站立状态')
this.unitItem.setState(new StandingState());
}
}, attackDuration);
}
// 重写方法
castSkill(): void {
console.log(this.unitItem, "单位已经进入释放技能状态");
}
}
// 击晕状态
export class StuningState extends BaseState {
getType(): UnitStateType {
return UnitStateType.Stuning;
}
enterState(unitItem: IUnitItem) {
super.enterState(unitItem);
this.stopCurrentAction();
}
// 重写方法
stun(): void {
console.log(this.unitItem, "单位已经进入击晕状态");
}
stopCurrentAction() {
console.log(this.unitItem, "单位所有动作停止,因为被击晕");
// 如果有正在进行的释放技能的操作,这里将其中断
// 这可能包括清除技能计时器、动画等
}
}
// 死亡状态
export class DeadState extends BaseState {
getType(): UnitStateType {
return UnitStateType.Dead;
}
enterState(unitItem: IUnitItem) {
super.enterState(unitItem);
this.stopCurrentAction();
}
// 重写方法
die() {
console.log(this.unitItem, "单位已经进入死亡状态");
}
stopCurrentAction() {
console.log(this.unitItem, "单位所有动作停止,因为已死亡");
// 如果有正在进行的释放技能的操作,这里将其中断
// 这可能包括清除技能计时器、动画等
}
}
接着是单位里的
export class UnitItem extends Component implements IItem, IUnitItem {
ad: number = 100;
mp: number = 0;
role: Fighter;
private currentState: IUnitState = null;
accept(visitor: IAttackVisitor) {
visitor.visitUnitItem(this)
}
setRole(role: Fighter): void {
this.role = role;
}
setState(state: IUnitState) {
this.currentState = state;
state.enterState(this);
}
getCurrentState(): IUnitState {
if (this.currentState == null) {
this.setState(new StandingState())
}
return this.currentState;
}
move() {
this.getCurrentState().move()
}
idle() {
this.getCurrentState().stand()
}
attack(unitItem: UnitItem<T>) {
if (!this.canAttack()) {
// 不能处理攻击的逻辑,可能是显示消息或者进入其他状态
return;
}
// 尝试进入攻击状态
this.getCurrentState().attack()
let damage = this.ad
let attackVisitor = new MonomerAttackVisitor(damage)
unitItem.accept(attackVisitor)
// 临时 todo 删除
console.log('假装本次攻击带有击晕效果')
unitItem.getCurrentState().stun()
}
skill() {
if (!this.canSkill()) {
// 不能处理攻击的逻辑,可能是显示消息或者进入其他状态
return;
}
// 尝试进入攻击状态
this.getCurrentState().castSkill()
}
die() {
this.getCurrentState().die()
}
private canSkill(): boolean {
// 检查单位是否可以进行技能攻击
// 例如,单位是否处于晕眩状态或者攻击是否冷却中
if (this.mp < 100) {
console.log('不能处理skill攻击的逻辑,因为魔法值不足100')
return false
}
if (this.getCurrentState().getType() == UnitStateType.CastSkilling) {
console.log('不能处理skill攻击的逻辑,因为已经处于技能释放中')
return false
}
if (this.getCurrentState().getType() == UnitStateType.Stuning) {
console.log('不能处理skill攻击的逻辑,因为已经被击晕')
return false
}
if (this.getCurrentState().getType() == UnitStateType.Dead) {
console.log('不能处理skill攻击的逻辑,因为已经死亡')
return false
}
return true;
}
private canAttack(): boolean {
// 检查单位是否可以进行攻击
// 例如,单位是否处于晕眩状态或者攻击是否冷却中
if (this.getCurrentState().getType() == UnitStateType.Attacking) {
console.log('不能处理攻击的逻辑,因为已经处于攻击中')
return false
}
if (this.getCurrentState().getType() == UnitStateType.Stuning) {
console.log('不能处理攻击的逻辑,因为已经被击晕')
return false
}
if (this.getCurrentState().getType() == UnitStateType.Dead) {
console.log('不能处理攻击的逻辑,因为已经死亡')
return false
}
return true;
}
}
在非状态对象类中使用时都是用以下的方式调用
this.getCurrentState().stand()
this.getCurrentState().move()
在方法里面会执行从当前状态到下一个状态所需要的动作
在状态类中,如果需要到下一个状态就需要再状态类中new一个新的状态,如
castSkill(): void {
console.log(this.unitItem, "单位准备进入释放技能状态");
this.unitItem.setState(new CastSkillingState());
}
接着在下一个状态CastSkillingState中的enterState,方法内其他动作
4、开始使用
let unitItem001 = xhgame.itemFactory.createUnitItem('kuloubing', UnitType.UnitSpine)
let unitItem002 = xhgame.itemFactory.createUnitItem('kuloubing', UnitType.UnitSpine)
unitItem001.idle()
unitItem002.idle()
unitItem002.skill()
unitItem002.mp = 100;
unitItem002.skill()
unitItem001.setRole(new Cavalry(new Sword()));
console.log('unitItem001(骑兵)准备使用【剑】对unitItem002发起了攻击')
unitItem001.attack(unitItem002)
unitItem001.setRole(new Cavalry(new Bow()));
console.log('unitItem001(骑兵)准备使用【弓】对unitItem002发起了攻击')
unitItem001.attack(unitItem002)