当前位置:   article > 正文

用23种设计模式打造一个cocos creator的游戏框架----(十二)状态模式_cocoscreator游戏框架

cocoscreator游戏框架

1、模式标准

模式名称:状态模式

模式分类:行为型

模式意图:允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。

结构图:

适用于:

1、一个对象的行为决定于它的状态,并且它必须在运行时刻根据状态改变它的行为。

2、一个操作中含有庞大的多分支的条件语句,且这些分支依赖丁该对象的状态。这个状态常用一个或多个枚举常量表示。通常,有多个操作包含这一相同的条件结构。State模式将每一个条件分支放入一个独立的类中。这使得开发者可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖于其他对象独立变化。

主要成员:

  • 上下文(Context):它定义了客户端感兴趣的接口,并且维护一个指向当前状态的实例变量。
  • 状态抽象(State):这是一个接口或者抽象类,它定义了每个状态必须实现的方法。
  • 具体状态(Concrete States):它们是实现状态接口的类,每个类对应一种状态,且包含了该状态下的行为实现。

2、分析与设计  

在一般的游戏开发中状态值通常是一个枚举值,但在状态模式中,状态值是一个通过实现了 IUnitState 接口的对象表示的。这种方法的优点是它更加灵活和强大,因为这个状态值不仅仅是一个值,它还是一组行为的集合(即方法实现)。这允许您在不同的状态之间切换行为,而不是仅仅改变一个表示状态的值。

在游戏中的单位一般有以下几种状态:站立,移动,攻击,释放技能中,眩晕中,死亡。比较常见的是单位正在释放一个技能,这个时候一个飞锤飞过来,将他击晕了,他停止了技能的释放。

接下来我们修改一下我们的意图

意图:允许一个对象(单位)在其内部状态改变时(由其状态对象来)改变它的行为。对象看起来似乎修改了它的类(实际是状态对象干的)。

3、开始打造

  1. export enum UnitStateType {
  2. Standing,
  3. Moving,
  4. Attacking,
  5. CastSkilling,
  6. Stuning,
  7. Die
  8. }
  1. export interface IUnitState {
  2. enterState(unitItem: IUnitItem): void
  3. //
  4. stand(): void; // 站立
  5. move(): void; // 移动
  6. attack(): void; // 攻击
  7. castSkill(): void; // 释放技能
  8. stun(): void; // 击晕
  9. die(): void; // 死亡
  10. //
  11. getType(): UnitStateType
  12. }
  1. // 状态基类,包含一个指向Unit的引用
  2. export abstract class BaseState implements IUnitState {
  3. protected unitItem: IUnitItem;
  4. enterState(unitItem: IUnitItem) {
  5. this.unitItem = unitItem;
  6. }
  7. // 获取状态的type值
  8. abstract getType(): UnitStateType;
  9. // 状态
  10. stand() {
  11. console.log(this.unitItem, "单位准备进入站立状态");
  12. this.unitItem.setState(new StandingState());
  13. }
  14. move() {
  15. console.log(this.unitItem, "单位准备进入移动状态");
  16. this.unitItem.setState(new MovingState());
  17. }
  18. attack(): void {
  19. console.log(this.unitItem, "单位准备进入攻击状态");
  20. this.unitItem.setState(new AttackingState());
  21. }
  22. castSkill(): void {
  23. console.log(this.unitItem, "单位准备进入释放技能状态");
  24. this.unitItem.setState(new CastSkillingState());
  25. }
  26. stun(): void {
  27. console.log(this.unitItem, "单位准备进入击晕状态");
  28. this.unitItem.setState(new StuningState());
  29. }
  30. die() {
  31. console.log(this.unitItem, "单位准备进入死亡状态");
  32. this.unitItem.setState(new DeadState());
  33. }
  34. }
  1. // 站立状态
  2. export class StandingState extends BaseState {
  3. getType(): UnitStateType {
  4. return UnitStateType.Standing;
  5. }
  6. // 重写方法
  7. stand() {
  8. console.log(this.unitItem, "单位已经进入站立状态");
  9. }
  10. }
  11. // 移动状态
  12. export class MovingState extends BaseState {
  13. getType(): UnitStateType {
  14. return UnitStateType.Moving;
  15. }
  16. // 重写方法
  17. move() {
  18. console.log(this.unitItem, "单位已经进入移动状态");
  19. }
  20. }
  21. // 攻击状态
  22. export class AttackingState extends BaseState {
  23. getType(): UnitStateType {
  24. return UnitStateType.Attacking;
  25. }
  26. enterState(unitItem: IUnitItem) {
  27. super.enterState(unitItem);
  28. this.doAction();
  29. }
  30. doAction() {
  31. // 执行攻击
  32. this.unitItem.role.attack(); // 攻击
  33. // 如果攻击顺利完成,进行清理并返回到正常状态
  34. // 例如,设置一个延时来模拟攻击动作的时间
  35. let attackDuration = 1000
  36. setTimeout(() => {
  37. if (this.unitItem.getCurrentState().getType() == UnitStateType.Attacking) {
  38. console.log('单位已从攻击状态到站立状态')
  39. this.unitItem.setState(new StandingState());
  40. }
  41. }, attackDuration);
  42. }
  43. // 重写方法
  44. attack(): void {
  45. console.log(this.unitItem, "单位已经进入攻击状态");
  46. }
  47. }
  48. // 释放技能状态
  49. export class CastSkillingState extends BaseState {
  50. getType(): UnitStateType {
  51. return UnitStateType.CastSkilling;
  52. }
  53. enterState(unitItem: IUnitItem) {
  54. super.enterState(unitItem);
  55. this.doAction();
  56. }
  57. doAction() {
  58. // 执行攻击
  59. // this.unitItem.role.attack(); // 攻击
  60. // 如果攻击顺利完成,进行清理并返回到正常状态
  61. // 例如,设置一个延时来模拟攻击动作的时间
  62. let attackDuration = 1000
  63. setTimeout(() => {
  64. if (this.unitItem.getCurrentState().getType() == UnitStateType.CastSkilling) {
  65. console.log('单位已从技能释放状态到站立状态')
  66. this.unitItem.setState(new StandingState());
  67. }
  68. }, attackDuration);
  69. }
  70. // 重写方法
  71. castSkill(): void {
  72. console.log(this.unitItem, "单位已经进入释放技能状态");
  73. }
  74. }
  75. // 击晕状态
  76. export class StuningState extends BaseState {
  77. getType(): UnitStateType {
  78. return UnitStateType.Stuning;
  79. }
  80. enterState(unitItem: IUnitItem) {
  81. super.enterState(unitItem);
  82. this.stopCurrentAction();
  83. }
  84. // 重写方法
  85. stun(): void {
  86. console.log(this.unitItem, "单位已经进入击晕状态");
  87. }
  88. stopCurrentAction() {
  89. console.log(this.unitItem, "单位所有动作停止,因为被击晕");
  90. // 如果有正在进行的释放技能的操作,这里将其中断
  91. // 这可能包括清除技能计时器、动画等
  92. }
  93. }
  94. // 死亡状态
  95. export class DeadState extends BaseState {
  96. getType(): UnitStateType {
  97. return UnitStateType.Dead;
  98. }
  99. enterState(unitItem: IUnitItem) {
  100. super.enterState(unitItem);
  101. this.stopCurrentAction();
  102. }
  103. // 重写方法
  104. die() {
  105. console.log(this.unitItem, "单位已经进入死亡状态");
  106. }
  107. stopCurrentAction() {
  108. console.log(this.unitItem, "单位所有动作停止,因为已死亡");
  109. // 如果有正在进行的释放技能的操作,这里将其中断
  110. // 这可能包括清除技能计时器、动画等
  111. }
  112. }

接着是单位里的

  1. export class UnitItem extends Component implements IItem, IUnitItem {
  2. ad: number = 100;
  3. mp: number = 0;
  4. role: Fighter;
  5. private currentState: IUnitState = null;
  6. accept(visitor: IAttackVisitor) {
  7. visitor.visitUnitItem(this)
  8. }
  9. setRole(role: Fighter): void {
  10. this.role = role;
  11. }
  12. setState(state: IUnitState) {
  13. this.currentState = state;
  14. state.enterState(this);
  15. }
  16. getCurrentState(): IUnitState {
  17. if (this.currentState == null) {
  18. this.setState(new StandingState())
  19. }
  20. return this.currentState;
  21. }
  22. move() {
  23. this.getCurrentState().move()
  24. }
  25. idle() {
  26. this.getCurrentState().stand()
  27. }
  28. attack(unitItem: UnitItem<T>) {
  29. if (!this.canAttack()) {
  30. // 不能处理攻击的逻辑,可能是显示消息或者进入其他状态
  31. return;
  32. }
  33. // 尝试进入攻击状态
  34. this.getCurrentState().attack()
  35. let damage = this.ad
  36. let attackVisitor = new MonomerAttackVisitor(damage)
  37. unitItem.accept(attackVisitor)
  38. // 临时 todo 删除
  39. console.log('假装本次攻击带有击晕效果')
  40. unitItem.getCurrentState().stun()
  41. }
  42. skill() {
  43. if (!this.canSkill()) {
  44. // 不能处理攻击的逻辑,可能是显示消息或者进入其他状态
  45. return;
  46. }
  47. // 尝试进入攻击状态
  48. this.getCurrentState().castSkill()
  49. }
  50. die() {
  51. this.getCurrentState().die()
  52. }
  53. private canSkill(): boolean {
  54. // 检查单位是否可以进行技能攻击
  55. // 例如,单位是否处于晕眩状态或者攻击是否冷却中
  56. if (this.mp < 100) {
  57. console.log('不能处理skill攻击的逻辑,因为魔法值不足100')
  58. return false
  59. }
  60. if (this.getCurrentState().getType() == UnitStateType.CastSkilling) {
  61. console.log('不能处理skill攻击的逻辑,因为已经处于技能释放中')
  62. return false
  63. }
  64. if (this.getCurrentState().getType() == UnitStateType.Stuning) {
  65. console.log('不能处理skill攻击的逻辑,因为已经被击晕')
  66. return false
  67. }
  68. if (this.getCurrentState().getType() == UnitStateType.Dead) {
  69. console.log('不能处理skill攻击的逻辑,因为已经死亡')
  70. return false
  71. }
  72. return true;
  73. }
  74. private canAttack(): boolean {
  75. // 检查单位是否可以进行攻击
  76. // 例如,单位是否处于晕眩状态或者攻击是否冷却中
  77. if (this.getCurrentState().getType() == UnitStateType.Attacking) {
  78. console.log('不能处理攻击的逻辑,因为已经处于攻击中')
  79. return false
  80. }
  81. if (this.getCurrentState().getType() == UnitStateType.Stuning) {
  82. console.log('不能处理攻击的逻辑,因为已经被击晕')
  83. return false
  84. }
  85. if (this.getCurrentState().getType() == UnitStateType.Dead) {
  86. console.log('不能处理攻击的逻辑,因为已经死亡')
  87. return false
  88. }
  89. return true;
  90. }
  91. }

 在非状态对象类中使用时都是用以下的方式调用

  1. this.getCurrentState().stand()
  2. this.getCurrentState().move()

   在方法里面会执行从当前状态到下一个状态所需要的动作

在状态类中,如果需要到下一个状态就需要再状态类中new一个新的状态,如

  1. castSkill(): void {
  2. console.log(this.unitItem, "单位准备进入释放技能状态");
  3. this.unitItem.setState(new CastSkillingState());
  4. }

接着在下一个状态CastSkillingState中的enterState,方法内其他动作

4、开始使用

  

  1. let unitItem001 = xhgame.itemFactory.createUnitItem('kuloubing', UnitType.UnitSpine)
  2. let unitItem002 = xhgame.itemFactory.createUnitItem('kuloubing', UnitType.UnitSpine)
  3. unitItem001.idle()
  4. unitItem002.idle()
  5. unitItem002.skill()
  6. unitItem002.mp = 100;
  7. unitItem002.skill()
  8. unitItem001.setRole(new Cavalry(new Sword()));
  9. console.log('unitItem001(骑兵)准备使用【剑】对unitItem002发起了攻击')
  10. unitItem001.attack(unitItem002)
  11. unitItem001.setRole(new Cavalry(new Bow()));
  12. console.log('unitItem001(骑兵)准备使用【弓】对unitItem002发起了攻击')
  13. unitItem001.attack(unitItem002)

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/菜鸟追梦旅行/article/detail/152435
推荐阅读
相关标签
  

闽ICP备14008679号