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

news2024/11/29 10:54:17

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;
    // 状态
    abstract stand(): void;
    abstract move(): void;
    abstract attack(): void;
    abstract castSkill(): void;
    abstract stun(): void;
    abstract die(): void;

}

// 站立状态
export class StandingState extends BaseState {

    getType(): UnitStateType {
        return UnitStateType.Standing;
    }

    stand() {
        console.log(this.unitItem, "单位已经进入站立状态");
    }

    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.setState(new DeadState());
    }

}

// 移动状态
export class MovingState extends BaseState {

    getType(): UnitStateType {
        return UnitStateType.Moving;
    }

    stand() {
        console.log(this.unitItem, "单位准备进入站立状态");
        this.unitItem.setState(new StandingState());
    }

    move() {
        console.log(this.unitItem, "单位已经进入移动状态");
    }

    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 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.getCurrentState().stand()
            }
        }, attackDuration);
    }
    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, "单位已经进入攻击状态");
    }
    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 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.getCurrentState().stand()
            }
        }, attackDuration);
    }

    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, "单位已经进入释放技能状态");
    }

    stun(): void {
        console.log(this.unitItem, "单位准备进入击晕状态");
        this.unitItem.setState(new StuningState());
    }

    die() {
        console.log(this.unitItem, "单位准备进入死亡状态");
        this.unitItem.setState(new DeadState());
    }

}


// 击晕状态
export class StuningState extends BaseState {

    getType(): UnitStateType {
        return UnitStateType.Stuning;
    }

    enterState(unitItem: IUnitItem) {
        super.enterState(unitItem);
        this.stopCurrentAction();
    }

    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, "单位已经进入击晕状态");
    }

    die() {
        console.log(this.unitItem, "单位准备进入死亡状态");
        this.unitItem.setState(new DeadState());
    }

    stopCurrentAction() {
        console.log(this.unitItem, "单位所有动作停止,因为被击晕");
        // 如果有正在进行的释放技能的操作,这里将其中断
        // 这可能包括清除技能计时器、动画等
    }

}
// 死亡状态
export class DeadState extends BaseState {

    getType(): UnitStateType {
        return UnitStateType.Dead;
    }

    enterState(unitItem: IUnitItem) {
        super.enterState(unitItem);
        this.stopCurrentAction();
    }

    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, "单位已经进入死亡状态");
    }

    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;
    }
}

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)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1299974.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Leetcode—219.存在重复元素II【简单】

2023每日刷题&#xff08;五十三&#xff09; Leetcode—219.存在重复元素II 实现代码 class Solution { public:bool containsNearbyDuplicate(vector<int>& nums, int k) {unordered_map<int, int> m;int n nums.size();for(int i 0; i < n; i) {if(m…

Hexo部署到云服务器后CSS样式无效的问题

Hexo部署到云服务器后CSS样式无效的问题 01 前言 趁活动入手了一个云服务器&#xff08;Linux&#xff09;&#xff0c;打算简单挂个博客上去&#xff0c;因为之前部署到github有了一些经验&#xff0c;所以还是选择使用Hexo。中间步骤略&#xff0c;部署完使用浏览器访问的时…

计算机基础知识66

Auth的补充 #概念&#xff1a;是django 的一个app&#xff0c;关于用户的登录&#xff0c;退出&#xff0c;注册... # 配置文件中配置&#xff1a;表会被迁移 INSTALLED_APPS [django.contrib.auth,] # auth有哪些表---权限控制&#xff1a; Permission&#xff1a;auth_permi…

m.2固态硬盘怎么选择?

一、什么是固态硬盘 固态硬盘又称SSD&#xff0c;是Solid State Drive的简称&#xff0c;由于采用了闪存技术&#xff0c;其处理速度远远超过传统的机械硬盘&#xff0c;这主要是因为固态硬盘的数据以电子的方式存储在闪存芯片中&#xff0c;不需要像机械硬盘那样通过磁头读写磁…

智能制造和低代码:打造高效工厂的关键

引言 随着全球制造业进入数字化时代&#xff0c;智能制造和低代码技术已经成为实现高效工厂运营的关键。这两个关键因素的融合为制造业带来了巨大的机会&#xff0c;使企业能够更灵活地应对市场需求、提高生产效率和降低成本。本文将深入探讨智能制造和低代码技术如何共同塑造…

Java到底是什么?学了我们能做什么?

一、Java是什么&#xff1f; Java是一门面向对象编程语言&#xff0c;不仅吸收了C语言的各种优点&#xff0c;还摒弃了C里难以理解的多继承、指针等概念&#xff0c;因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表&#xff0c;极好地实…

力扣257. 二叉树的所有路径(递归回溯与迭代)

题目&#xff1a; 给你一个二叉树的根节点 root &#xff0c;按 任意顺序 &#xff0c;返回所有从根节点到叶子节点的路径。 叶子节点 是指没有子节点的节点。 示例 1&#xff1a; 输入&#xff1a;root [1,2,3,null,5] 输出&#xff1a;["1->2->5","…

Java简易版:UDP协议实现群聊

要先 运行服务端&#xff0c;在运行客户端&#xff0c;否则会报错。 服务端&#xff1a; package 二十一章;import java.io.*; import java.net.*; import java.util.ArrayList; public class T{public static ServerSocket server_socket;public static ArrayList<Socket…

CMake是什么

文章目录 一.什么是CMake二.CMake安装三.CMake一个HelloWord-的语法介绍3.1 PROJECT关键字3.2 SET关键字3.3 MESSAGE关键字3.4 ADD_EXECUTABLE关键字3.5 include_directories关键字3.6 aux_source_directory 四.语法的基本原则4.1 语法注意事项 五.内部构建和外部构建5.1 外部构…

Python:核心知识点整理大全11-笔记

目录 ​编辑 6.2.4 修改字典中的值 6.2.5 删除键—值对 注意 删除的键—值对永远消失了。 6.2.6 由类似对象组成的字典 6.3 遍历字典 6.3.1 遍历所有的键—值对 6.3.2 遍历字典中的所有键 往期快速传送门&#x1f446;&#xff08;在文章最后&#xff09;&#xff1a; 6.…

file-saver 的使用

简介 FileSaver.js 是在客户端保存文件的解决方案&#xff0c;非常适合在客户端生成文件的 Web 应用程序 基本使用 以下内容基于官方文档&#xff0c;官方文档传送门https://gitcode.net/mirrors/eligrey/FileSaver.js 注意&#xff1a;存在文件保存的大小限制&#xff0c;具…

入门深度学习真的这么难吗?

今天微信私信回答了一个同学的问题&#xff1a;&#xff1f;我在入门深度学习的过程中&#xff0c;从配环境到 debug 全是坑&#xff0c;解决问题的时间远超跑模型的时间&#xff0c;为什么入门深度学习这么难&#xff1f; 这个问题真的问到我的心坎里了&#xff0c;我只能说&…

Python轴承故障诊断 (二)连续小波变换CWT

目录 前言 1 连续小波变换CWT原理介绍 1.1 CWT概述 1.2 CWT的原理和本质 2 基于Python的CWT实现与参数对比 2.1 代码示例 2.2 参数介绍和选择策略 2.2.1 尺度长度&#xff1a; 2.2.2 小波函数&#xff08;wavelet&#xff09;&#xff1a; 2.3 凯斯西储大学轴承数据的…

御剑工具学习

御剑 1.1 工具的下载路径1.2 工具的安装流程1.3 工具的详细使用 1.1 工具的下载路径 百度网盘 链接&#xff1a;https://pan.baidu.com/s/1Bn7GtWb7AStcjzVahFOjSQ 提取码&#xff1a;zkaq 1.2 工具的安装流程 御剑不用安装&#xff0c;直接下载下来解压&#xff0c;双击“御…

【Java实现百钱买百鸡的两种写法】

Java实现百钱买百鸡的两种写法 Java双重嵌套for循环实现百钱买百鸡的写法&#xff08;一&#xff09;Java三重嵌套for循环实现百钱买百鸡的写法&#xff08;二&#xff09; Java双重嵌套for循环实现百钱买百鸡的写法&#xff08;一&#xff09; //定义一个记录循环次数变量int …

C语言指针基础题(一)

目录 例题一题目解析答案 例题二题目解析答案 例题三题目解析答案 例题四题目解析答案 例题五题目解析答案 例题六题目解析答案 例题七题目解析答案 感谢各位大佬对我的支持,如果我的文章对你有用,欢迎点击以下链接 &#x1f412;&#x1f412;&#x1f412; 个人主页 &#x…

202301209将RK3399的挖掘机开发板在Android10下设置系统默认为24小时制

202301209将RK3399的挖掘机开发板在Android10下设置系统默认为24小时制 2023/12/9 22:07 应该也可以适用于RK3399的Android12系统 --- a/frameworks/base/packages/SettingsProvider/res/values/defaults.xml b/frameworks/base/packages/SettingsProvider/res/values/default…

docker安装node及使用

文章目录 一、安装node二、创建node容器三、进入创建的容器如有启发&#xff0c;可点赞收藏哟~ 一、安装node 查看可用版本 docker search node安装最新版本 docker install node:latest二、创建node容器 docker run -itd --name node-test node–name node-test&#xff1…

10款装系统启动工具

1、GhostWin10 PE&#xff1a; GhostWin10 PE主要适用于安装Windows 10操作系统。它是一个基于Ghost Win10制作的PE系统&#xff0c;提供了安装Windows 10的功能 2、老毛桃PE&#xff1a; 老毛桃PE适用于各种Windows操作系统的安装和修复。它是一个基于Windows PE环境的工具…

epoll实现同时承载100w客户端的数量

概念 先表明&#xff0c;这里是让epoll能够同时承受100w的连接&#xff0c;不针对业务处理。 对于百万并发的业务处理&#xff0c;其前提条件就是要同时承受住100w的连接。 程序源码 epoll的源码直接给出来 /*支持百万并发的 reactor1.其主要限制在于Linux系统的限制,需要修改一…