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

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)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

记录一次云原生线上服务数据迁移全过程

文章目录 背景迁移方案调研迁移过程服务监控脚本定时任务暂停本地副本服务启动&#xff0c;在线服务下线MySQL 数据迁移Mongo 数据迁移切换新数据库 ip 本地服务启动数据库连接验证服务打包部署服务重启前端恢复正常监控脚本定时任务启动旧服务器器容器关闭 迁移总结 背景 校园…

代码随想录二刷 |二叉树 |101. 对称二叉树

代码随想录二刷 &#xff5c;二叉树 &#xff5c;101. 对称二叉树 题目描述解题思路 & 代码实现递归法迭代法使用队列使用栈 题目描述 101.对称二叉树 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 示例 1&#xff1a; 输入&#xff1a;root [1,2,2,…

adb unauthorized 踩坑记录

给Realme X7 Pro 安装Root后&#xff0c;发现adb连接设备呈现unauthorized 状态&#xff1a; 在Google以后&#xff0c;尝试了很多方案&#xff0c;均无效&#xff0c;尝试的方案如下&#xff1a; 重启手机&#xff0c;电脑。不行撤销调试授权&#xff0c;开关usb调试&#xf…

持续集成交付CICD:Jenkins配置Nexus制品发布

目录 一、实验 1.Jenkins配置Nexus制品发布 一、实验 1.Jenkins配置Nexus制品发布 &#xff08;1&#xff09;策略 发布其实就是下载制品&#xff0c;然后将制品发送到目标主机&#xff0c;最后通过脚本或者指令启动程序。 &#xff08;2&#xff09;安装Maven Artifact …

基于JavaWeb+SSM+Vue马拉松报名系统微信小程序的设计和实现

基于JavaWebSSMVue马拉松报名系统微信小程序的设计和实现 源码获取入口Lun文目录前言主要技术系统设计功能截图订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 源码获取入口 Lun文目录 1系统概述 1 1.1 研究背景 1 1.2研究目的 1 1.3系统设计思想 1 2相关技术 2 2.…

SQL命令---添加新字段

介绍 使用sql语句为表添加新字段。 命令 alter table 表名 add 新字段名 数据类型;例子 向a表中添加name字段&#xff0c;类型为varchar(255)。 alter table a add name varchar(255);下面是执行添加有的表结构&#xff1a;

react之项目打包,本地预览,路由懒加载,打包体积分析以及如何配置CDN

react之项目打包,本地预览,路由懒加载,打包体积分析以及如何配置CDN 一、项目打包二、项目本地预览三、路由懒加载四、打包体积分析五、配置CDN 一、项目打包 执行命令 npm run build根目录下生成的build文件夹 及时打包后的文件 二、项目本地预览 1.全局安装本地服务包 npm…

内存分配器

实现分配器需要考虑的问题 空闲块的组织方式&#xff1a;如何记录现有的空闲块空闲块的选择&#xff1a;如何选择一个合适的空闲块空闲块的分割&#xff1a;选择了一个合适的空闲块后如何处理空闲块内部的剩余部分空闲块的合并&#xff1a;如何处理一个刚刚被释放的块&#xf…

Python sqlalchemy使用

基本结构 #!/usr/bin/python3 # -*- coding:utf-8 -*- """ author: JHC file: base_db.py time: 2023/6/19 21:34 desc: """ from sqlalchemy import create_engine,text from sqlalchemy.orm import sessionmaker,scoped_session from contex…

计算机服务器中了Mallox勒索病毒怎么解密,Mallox勒索病毒解密步骤

计算机网络技术的不断发展与应用&#xff0c;为企业的生产运营提供了坚实的基础&#xff0c;大大提高了企业的生产与工作效率&#xff0c;但随之而来的网络安全威胁也在不断增加。在本月&#xff0c;云天数据恢复中心接到了很多企业的求助&#xff0c;企业的计算机服务器遭到了…

Nacos源码解读12——Nacos中长连接的实现

短连接 VS 长连接 什么是短连接 客户端和服务器每进行一次HTTP操作&#xff0c;就建立一次连接&#xff0c;任务结束就中断连接。 长连接 客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭&#xff0c;客户端再次访问这个服务器时&#xff0c;会继续使用这一条已经建立…

数理统计基础:参数估计与假设检验

在学习机器学习的过程中&#xff0c;我充分感受到概率与统计知识的重要性&#xff0c;熟悉相关概念思想对理解各种人工智能算法非常有意义&#xff0c;从而做到知其所以然。因此打算写这篇笔记&#xff0c;先好好梳理一下参数估计与假设检验的相关内容。 1 总体梳理 先从整体结…

【Spring 基础】00 入门指南

【Spring 基础】00 入门指南 文章目录 【Spring 基础】00 入门指南1.简介2.概念1&#xff09;控制反转&#xff08;IoC&#xff09;2&#xff09;依赖注入&#xff08;DI&#xff09; 3.核心模块1&#xff09;Spring Core2&#xff09;Spring AOP3&#xff09;Spring MVC4&…

用python 网络自动化统计交换机有多少端口UP

用python统计交换机有多少端口UP 用python统计交换机有多少端口UP&#xff0c;可以间接的反馈有多少个用户在线。我们使用上次的脚本将可达的网络设备ip统计到reachable_ip.txt中&#xff0c;这次我们使用reachable_ip.txt来登陆设备来统计多少端口是UP的 云配置 拓扑 交换机…

ModuleNotFoundError: No module named ‘docx‘

ModuleNotFoundError: No module named ‘docx’ 文章目录 ModuleNotFoundError: No module named docx背景报错问题报错翻译报错位置代码报错原因解决方法今天的分享就到此结束了 背景 在使用之前的代码时&#xff0c;报错&#xff1a; Traceback (most recent call last): Fi…

循环依赖:解析软件设计的迷局

目录 引言 循环依赖的本质 影响与挑战 1. 编译和构建问题 2. 耦合度增加 3. 难以进行单元测试 4. 可扩展性降低 解决循环依赖的策略 1. 模块重构 2. 引入接口抽象 3. 依赖注入 4. 模块化与分层设计 5. 使用工具进行分析 实际案例&#xff1a;Spring框架的循环依赖…

Redis高效恢复策略:内存快照与AOF

第1章&#xff1a;Redis宕机恢复的重要性和挑战 大家好&#xff0c;我是小黑。今天咱们来聊聊Redis宕机后的恢复策略。想象一下&#xff0c;你的网站突然宕机了&#xff0c;所有的数据都飘了&#xff0c;这种情况下&#xff0c;快速恢复数据就显得尤为重要。Redis作为一个高性…

令牌桶算法理解学习(限流算法)

令牌桶算法是网络流量整形&#xff08;Traffic Shaping&#xff09;和速率限制&#xff08;Rate Limiting&#xff09;中最常使用的一种算法。典型情况下&#xff0c;令牌桶算法用来控制发送到网络上的数据的数目&#xff0c;并允许突发数据的发送。 用简单的话语来说就是限制…

研表究明,文字的序顺并不定一能响影GPT-4读阅

深度学习自然语言处理 原创作者&#xff1a;yy 很多年前&#xff0c;你一定在互联网上看过这张图&#xff0c;展示了人脑能够阅读和理解打乱顺序的单词和句子&#xff01;而最近东京大学的研究发现&#xff0c;大语言模型&#xff08;LLMs&#xff09; 尤其是 GPT-4&#xff0c…

STM32 标准外设SPL库、硬件抽象层HAL库、低层LL库区别?

1、STM32 之一 HAL库、标准外设库、LL库_ZCShou的博客-CSDN博客_ll库&#xff08;仔细阅读&#xff09; 2、STM32标准外设库、 HAL库、LL库 - King先生 - 博客园 3、STM32 之 HAL库_戈 扬的博客&#xff08;仔细阅读&#xff09; 4、STM32 LL 为什么比 HAL 高效&#xff1…