『功能项目』怪物的有限状态机【42】
本章项目成果展示
我们打开上一篇41项目优化 - 框架加载资源的项目,
本章要做的事情是按照框架的思想构建项目并完成怪物的自动巡逻状态,当主角靠近怪物时,怪物会朝向主角释放技能
首先新建脚本:BossCtrl.cs
(通常把xxxCtrl.cs脚本写在中间层,后续会增加xxxOpt.cs脚本进行调用xxxCtrl.cs中的函数)
(xxxOpt.cs脚本在上层调用)
using UnityEngine;
public class BossCtrl : MonoBehaviour{protected bool isDead;Animator animator;public int hp;public int currentHp;public int attackValue;public int defineValue;public void Init() {isDead = false;hp = currentHp = 1000;attackValue = 700;defineValue = 700;animator = GetComponent<Animator>();animator.SetBool("IsMoving", false);}
}
将控制层(xxxCtrl.cs)增加在资源框架脚本中:
此时怪物身上就有了血量攻击力防御力等数据
接下来再增加一个xxxOpt.cs脚本调用xxxCtrl.cs脚本的数据以及通用机制(此时xxxCtrl.cs还没有写通用机制,可以理解为后续将机制写在xxxCtrl.cs中 而调用写在xxxOpt.cs脚本中)
新建脚本:BossOpt.cs
using UnityEngine;
public class BossOpt : MonoBehaviour{FSM fsm;public BossBlackboard blackboard;Vector3 playerPos;Vector3 selfPos;Transform blackboardTransform;Vector3 blackboardTargetPos;Animator animator;public void Start(){blackboardTransform = GameObject.Find("Boss01").gameObject.transform;blackboardTargetPos = new Vector3 (0, 45, 15);blackboard = new BossBlackboard(5,3, blackboardTransform, blackboardTargetPos, transform.position);fsm = new FSM(blackboard);fsm.AddState(StateType.Idle, new AI_IdleState(fsm));fsm.AddState(StateType.Move, new AI_MoveState(fsm));blackboard.initPos = transform.position; fsm.SwitchState(StateType.Idle);animator = GetComponent<Animator>();}void Update(){selfPos = transform.position;playerPos = GameObject.Find("PlayerNormal").gameObject.transform.position;if (Vector3.Distance(selfPos, playerPos) < 10) {animator.SetBool("IsSkill", true);transform.LookAt(playerPos + new Vector3(0,3,0)); }if (Vector3.Distance(selfPos, playerPos) >= 10) {animator.SetBool("IsSkill", false);fsm.OnUpdate();transform.LookAt(blackboard.targetPos);}}
}
此时会有很多红色报错,因为少了一些自定义的类,接下来我们创建FSM类
也就是说BossOpt.cs是调用FSM(有限状态机)类的脚本,接下来我们需要写有限状态机类:
新建脚本:Blackboard.cs
using System;
[Serializable]
public class Blackboard{//此处存储共享数据 或者向外展示的数据 可配置数据
}
新建脚本:BossBlackboard.cs
using System;
using UnityEngine;
[Serializable]
public class BossBlackboard : Blackboard{//闲置时间public float idleTime;public float moveSpeed;public Transform transform;public Vector3 targetPos;public Vector3 initPos;public BossBlackboard(float idleTime, float moveSpeed,Transform transform, Vector3 targetPos, Vector3 initPos){this.idleTime = idleTime;this.moveSpeed = moveSpeed;this.transform = transform;this.targetPos = targetPos;this.initPos = initPos;}
}
新建脚本:IState.cs
public interface IState{void OnEnter();void OnExit();void OnUpdate();
}
新建脚本:FSM.cs
using System.Collections.Generic;
public enum StateType{Idle,Move,
}
public class FSM {public IState curState;public Dictionary<StateType, IState> states;public Blackboard blackboard;public FSM(Blackboard blackboard) {this.states = new Dictionary<StateType, IState>();this.blackboard = blackboard;}//外部使用 - 增加状态public void AddState(StateType stateType, IState state) {if (states.ContainsKey(stateType)) {return;}states.Add(stateType,state);}//外部使用 - 切换状态public void SwitchState(StateType stateType) {if (!states.ContainsKey(stateType)) {return;}if (curState != null) {curState.OnExit();}curState = states[stateType];curState.OnEnter();}public void OnUpdate() {curState.OnUpdate();}
}
新建脚本:AI_IdleState.cs
using UnityEngine;
public class AI_IdleState : IState{//闲置计时器public float idleTimer;public AI_IdleState(FSM fsm){this.fsm = fsm;blackboard = fsm.blackboard as BossBlackboard;}FSM fsm;BossBlackboard blackboard;public void OnEnter(){idleTimer = 0;}public void OnUpdate(){idleTimer += Time.deltaTime;if (idleTimer > blackboard.idleTime){fsm.SwitchState(StateType.Move);}}public void OnExit() { }
}
新建脚本:AI_MoveState.cs
using UnityEngine;
public class AI_MoveState : IState{Animator animator;public float idleTimer;FSM fsm;BossBlackboard blackboard;public AI_MoveState(FSM fsm){this.fsm = fsm;blackboard = fsm.blackboard as BossBlackboard;}public void OnEnter(){animator = GameObject.Find("Boss01").GetComponent<Animator>();float randomAngle = Random.Range(0, 360);float randomRadius = Random.Range(0, 7);blackboard.targetPos = new Vector3(blackboard.initPos.x + Mathf.Cos(Mathf.Deg2Rad * randomAngle) * randomRadius,blackboard.transform.position.y,blackboard.initPos.z + Mathf.Sin(Mathf.Deg2Rad * randomAngle) * randomRadius);}public void OnExit() { }public void OnUpdate(){if (Vector3.Distance(blackboard.transform.position, blackboard.targetPos) < 0.1f){fsm.SwitchState(StateType.Idle);animator.SetBool("IsMoving", false);}else{blackboard.transform.position = Vector3.MoveTowards(blackboard.transform.position,blackboard.targetPos, blackboard.moveSpeed * Time.deltaTime);animator.SetBool("IsMoving", true);}}
}
保存代码将调用FSM(有限状态机类)的BossOpt.cs脚本增加到GameManager.cs资源框架上
运行项目 - Boss就会Idle状态5秒钟后随机移动任意方向5秒钟进行循环并且不会超过以自身为原点半径为7的圆范围
本章利用有限状态机FSM做了Idle与Move下的转换,并且当主角靠近怪物时 怪物会释放技能
接下来利用前几章的知识增加一些脚本,增加技能特效,怪物UI信息,以及伤害计算让主角持续掉血
首先创建怪物UI信息
以前文章有制作教程
将UI对象放在指定文件夹下
之前导入的技能包中可找到该技能或者重新导入个新技能修改其名字放进指定文件夹即可
修改脚本:
using System.Collections;
using UnityEngine;
public class BossOpt : MonoBehaviour{FSM fsm;public BossBlackboard blackboard;Vector3 playerPos;Vector3 selfPos;Transform blackboardTransform;Vector3 blackboardTargetPos;Animator animator;#region UI信息GameObject infoUIPrefab;GameObject infoUIInstance;bool Count;#endregion#region 技能特效GameObject boss01SkillPrefab;#endregion#region 伤害计算GameManager gm;BossCtrl bossCtrl;#endregionpublic void Start(){blackboardTransform = GameObject.Find("Boss01").gameObject.transform;blackboardTargetPos = new Vector3 (0, 45, 15);blackboard = new BossBlackboard(5,3, blackboardTransform, blackboardTargetPos, transform.position);fsm = new FSM(blackboard);fsm.AddState(StateType.Idle, new AI_IdleState(fsm));fsm.AddState(StateType.Move, new AI_MoveState(fsm));blackboard.initPos = transform.position; fsm.SwitchState(StateType.Idle);animator = GetComponent<Animator>();#region UI信息infoUIPrefab = Resources.Load<GameObject>("Prefabs/Images/Boss01UI");Count = false;#endregion#region 技能特效boss01SkillPrefab = Resources.Load<GameObject>("Prefabs/Effects/Boss01Effect");#endregion#region 伤害计算gm = GameManager.Instance;bossCtrl = gameObject.GetComponent<BossCtrl>();#endregion}void Update(){selfPos = transform.position;playerPos = GameObject.Find("PlayerNormal").gameObject.transform.position;if (Vector3.Distance(selfPos, playerPos) < 10) {animator.SetBool("IsSkill", true);transform.LookAt(playerPos + new Vector3(0,3,0));#region UI信息 -> 技能特效if (!Count) {infoUIInstance = Instantiate(infoUIPrefab, new Vector3(0f, -50f, 0f), Quaternion.identity);infoUIInstance.transform.SetParent(GameObject.Find("CurrentCanvas").transform, false);Count = true;infoUIInstance.AddComponent<Boss01UIInfo>();#region 技能特效StartCoroutine(WaitTwoSStartBoss01Skill());#endregion}#endregion}if (Vector3.Distance(selfPos, playerPos) >= 10) {animator.SetBool("IsSkill", false);fsm.OnUpdate();transform.LookAt(blackboard.targetPos);#region UI信息 -> 技能特效if (Count) {Destroy(infoUIInstance);Count = false;#region 技能特效Destroy(GameObject.Find("Boss01Effect(Clone)").gameObject);#endregion}#endregion}}#region 技能特效IEnumerator WaitTwoSStartBoss01Skill(){yield return new WaitForSeconds(2);Instantiate(boss01SkillPrefab, transform.position, transform.localRotation);while (true) {gm.infoSys.playerCurrentHP -= bossCtrl.attackValue - gm.infoSys.defineValue;yield return new WaitForSeconds(2);if (Vector3.Distance(selfPos, playerPos) >= 10)break;}}#endregion
}
新建脚本:Boss01UIInfo.cs
using UnityEngine;
using UnityEngine.UI;
public class Boss01UIInfo : MonoBehaviour{BossCtrl bossCtrl;Slider hp;void Start(){bossCtrl = FindObjectOfType<BossCtrl>();hp = transform.Find("Slider").GetComponent<Slider>();}void Update(){if (hp != null)hp.value = bossCtrl.currentHp;}
}
运行项目即可实现 - 主角不在怪物攻击范围之内 ,怪物进行停留五秒随机行走三秒但不会超过半径为7米的圆范围, 一旦主角进入敌人范围,怪物会朝向主角释放技能,生成怪物UI信息,并且每次释放技能会让主角失去一定血量,当主角离开范围内,怪物回到巡逻状态
本章做了主角不在怪物攻击范围之内 ,怪物进行停留五秒随机行走三秒但不会超过半径为7米的圆范围, 一旦主角进入敌人范围,怪物会朝向主角释放技能,生成怪物UI信息,并且每次释放技能会让主角失去一定血量,当主角离开范围内,怪物回到巡逻状态的功能
接下来要实现:
1.战士职业平A(按A键)使怪物掉血的功能
2.窗口可拖拽脚本
3.点击名称寻找地点功能
4.隐藏怪物的生成
5.怪物I攻击范围内的主动攻击
6.掉落坐骑蛋的获取
7.异步传送转换场景
以及开放回合制、坐骑系统、宠物系统、背包系统、神炼系统、商城系统、Boss的目标跟随任务导航系统以及UI播放3D动画效果等等。
具体项目运行效果请关注water1024的b站视频项目演示《破碎纪元》
【Unity回合2.5D】破碎纪元_单机游戏热门视频 (bilibili.com)https://www.bilibili.com/video/BV1rZY4e9Ebs/?spm_id_from=333.999.0.0&vd_source=547091a95b03acfa8e8a9e46ef499cd6
相关文章:

『功能项目』怪物的有限状态机【42】
本章项目成果展示 我们打开上一篇41项目优化 - 框架加载资源的项目, 本章要做的事情是按照框架的思想构建项目并完成怪物的自动巡逻状态,当主角靠近怪物时,怪物会朝向主角释放技能 首先新建脚本:BossCtrl.cs (通常把xxxCtrl.cs脚…...

【C++】模板进阶:深入解析模板特化
C语法相关知识点可以通过点击以下链接进行学习一起加油!命名空间缺省参数与函数重载C相关特性类和对象-上篇类和对象-中篇类和对象-下篇日期类C/C内存管理模板初阶String使用String模拟实现Vector使用及其模拟实现List使用及其模拟实现容器适配器Stack与Queue 本章将…...

Python数据分析-世界上最富有的1000人
一、研究背景 随着全球化的加速发展和技术的进步,财富分配问题日益成为全球关注的焦点。财富的不平等现象日益明显,少数极富有的个人掌握了全球大部分的财富资源。了解全球最富有个人的财富分布及其背后的行业和国家因素,对于分析全球经济趋…...
CSS中隐藏滚动条的同时保留滚动功能
在CSS中,我们可以通过一些技巧来隐藏滚动条,同时保留滚动功能。以下是几种常用的方法和具体的实现步骤。 1. 使用 overflow 和 ::-webkit-scrollbar 这种方法适用于大多数现代浏览器。通过设置 overflow 属性启用滚动,同时利用 ::-webkit-s…...

我的标志:奇特的头像
<!DOCTYPE html> <html lang="zh-CN"> <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>与妖为邻</title><style>figu…...

中国空间计算产业链发展分析
2024中国空间计算产业链拆解 空间计算设备主要包括AR、VR、MR等终端设备。VR设备通常包括头戴式显示器(VR头盔)、手柄或追踪器等组件,用以完全封闭用户视野,营造虚拟环境体验。这些设备配备高分辨率显示屏、内置传感器和跟踪器。 …...

DAY14信息打点-JS 架构框架识别泄漏提取API 接口枚举FUZZ 爬虫插件项目
本课意义: 1.如何从表现中的JS提取价值信息 2.如何从地址中FUZZ提取未知的JS文件 3.如何从JS开放框架WebPack进行测试 一、JS 前端架构-识别&分析 在JS中寻找更多的URL地址,在JS代码逻辑(加密算法、APIKey配置、验证逻辑)中进…...

TS - tsconfig.json 和 tsconfig.node.json 的关系,如何在TS 中使用 JS 不报错
目录 1,前言2,二者关系2.1,使用 3,遇到的问题3.1,TS 中使用 JS 1,前言 通过 Vite 创建的 Vue3 TS 项目,根目录下会有 tsconfig.json 和 tsconfig.node.json 文件,并且存在引用关系…...

revisiting拉普拉斯模板
二维向量的二阶微分是Hessian矩阵,拉普拉斯算子是将两个独立的二阶微分求和,对二阶微分的近似。 我不认同冈萨雷斯的8邻域拉普拉斯模板。 MATLAB图像处理工具箱中fspecial函数’laplacian’参数给的拉普拉斯模板: 对于数字滤波器ÿ…...

深入分析计算机网络性能指标
速率带宽吞吐量时延时延带宽积往返时间RTT利用率丢包率图书推荐内容简介作者简介 速率 连接在计算机网络上的主机在数字信道上传送比特的速率,也称为比特率或数据率。 基本单位:bit/s(b/s、bps) 常用单位:kb/s&#x…...

pyflink 安装和测试
FPY Warning! 安装 apache-Flink # pip install apache-Flink -i https://pypi.tuna.tsinghua.edu.cn/simple/ Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple/ Collecting apache-FlinkDownloading https://pypi.tuna.tsinghua.edu.cn/packages/7f/a3/ad502…...
《网络故障处理案例:公司网络突然中断》
网络故障处理案例:公司网络突然中断 一、故障背景 某工作日上午,一家拥有 500 名员工的公司突然出现整个网络中断的情况。员工们无法访问互联网、内部服务器和共享文件,严重影响了工作效率。 二、故障现象 1. 所有员工的电脑…...
JavaSE:9、数组
1、一维数组 初始化 import com.test.*;public class Main {public static void main(String [] argv){int a[]{1,2};int b[]new int[]{1,0,2};// int b[]new int[3]{1,2,3}; ERROR 要么指定长度要么大括号里初始化数据算长度int[] c{1,2};int d[]new int[10];} }基本类型…...

【裸机装机系列】2.kali(ubuntu)-裸机安装kali并进行磁盘分区-2024.9最新
【前言】 2024年为什么弃用ubuntu,请参考我写的另一篇博文:为什么不用ubuntu,而选择基于debian的kali操作系统-2024.9最新 【镜像下载】 1、镜像下载地址 https://www.kali.org/get-kali/选择installer-image,进入界面下载相应的ISO文件 我…...

解决:Vue 中 debugger 不生效
目录 1,问题2,解决2.1,修改 webpack 配置2.2,修改浏览器设置 1,问题 在 Vue 项目中,可以使用 debugger 在浏览器中开启调试。但有时却不生效。 2,解决 2.1,修改 webpack 配置 通…...

Mac笔记本上查看/user/目录下的文件的几种方法
在Mac笔记本上查看/user/下的文件,可以通过多种方法实现。以下是一些常见的方法: 一、使用Finder 打开Finder:点击Dock栏中的Finder图标,或者使用快捷键Command F。 导航到用户目录: 在Finder的菜单栏中࿰…...
工程师 - ACPI和ACPICA的区别
ACPI(高级配置和电源接口)和 ACPICA(ACPI 组件架构)密切相关,但在系统电源管理和配置方面却有不同的作用。以下是它们的区别: ACPI(高级配置和电源接口) - 定义: ACPI 是…...
一文快速上手-create-vue脚手架
文章目录 初识 create-vuecreate-vue新建项目Vue.js 3 项目目录结构项目的运行和打包vite.config.js文件解析其他:webpack和Vite的区别 初识 create-vue create-vue类似于Vue CLI脚手架,可以快速创建vuejs 3项目,create-vue基于Vite。Vite支…...
笔记整理—内核!启动!—kernel部分(7)rcs文件和登录部分与密码解析
该文件的位置在/etc/init.d/rcs,前文说过这个是一个配置文件,最开始的地方首先就是PATH相关的用export导出相关的PATH做环境变量,将可执行路径导为PATH执行时就不用写全路径了,该位置的PATH路径导出了/bin、/sbin、/usr/bin、/usr…...
朴素贝叶斯 (Naive Bayes)
朴素贝叶斯 (Naive Bayes) 通俗易懂算法 朴素贝叶斯(Naive Bayes)是一种基于概率统计的分类算法。它的核心思想是通过特征的条件独立性假设来简化计算复杂度,将复杂的联合概率分布分解为特征的独立概率分布之积。 基本思想 朴素贝叶斯基于…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者
抖音增长新引擎:品融电商,一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中,品牌如何破浪前行?自建团队成本高、效果难控;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...

微服务商城-商品微服务
数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...

微信小程序云开发平台MySQL的连接方式
注:微信小程序云开发平台指的是腾讯云开发 先给结论:微信小程序云开发平台的MySQL,无法通过获取数据库连接信息的方式进行连接,连接只能通过云开发的SDK连接,具体要参考官方文档: 为什么? 因为…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...

AI,如何重构理解、匹配与决策?
AI 时代,我们如何理解消费? 作者|王彬 封面|Unplash 人们通过信息理解世界。 曾几何时,PC 与移动互联网重塑了人们的购物路径:信息变得唾手可得,商品决策变得高度依赖内容。 但 AI 时代的来…...
音视频——I2S 协议详解
I2S 协议详解 I2S (Inter-IC Sound) 协议是一种串行总线协议,专门用于在数字音频设备之间传输数字音频数据。它由飞利浦(Philips)公司开发,以其简单、高效和广泛的兼容性而闻名。 1. 信号线 I2S 协议通常使用三根或四根信号线&a…...

使用SSE解决获取状态不一致问题
使用SSE解决获取状态不一致问题 1. 问题描述2. SSE介绍2.1 SSE 的工作原理2.2 SSE 的事件格式规范2.3 SSE与其他技术对比2.4 SSE 的优缺点 3. 实战代码 1. 问题描述 目前做的一个功能是上传多个文件,这个上传文件是整体功能的一部分,文件在上传的过程中…...
6️⃣Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙
Go 语言中的哈希、加密与序列化:通往区块链世界的钥匙 一、前言:离区块链还有多远? 区块链听起来可能遥不可及,似乎是只有密码学专家和资深工程师才能涉足的领域。但事实上,构建一个区块链的核心并不复杂,尤其当你已经掌握了一门系统编程语言,比如 Go。 要真正理解区…...