当前位置: 首页 > news >正文

『功能项目』怪物的有限状态机【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)icon-default.png?t=O83Ahttps://www.bilibili.com/video/BV1rZY4e9Ebs/?spm_id_from=333.999.0.0&vd_source=547091a95b03acfa8e8a9e46ef499cd6

http://www.lryc.cn/news/439411.html

相关文章:

  • 【C++】模板进阶:深入解析模板特化
  • Python数据分析-世界上最富有的1000人
  • CSS中隐藏滚动条的同时保留滚动功能
  • 我的标志:奇特的头像
  • 中国空间计算产业链发展分析
  • DAY14信息打点-JS 架构框架识别泄漏提取API 接口枚举FUZZ 爬虫插件项目
  • TS - tsconfig.json 和 tsconfig.node.json 的关系,如何在TS 中使用 JS 不报错
  • revisiting拉普拉斯模板
  • 深入分析计算机网络性能指标
  • pyflink 安装和测试
  • 《网络故障处理案例:公司网络突然中断》
  • JavaSE:9、数组
  • 【裸机装机系列】2.kali(ubuntu)-裸机安装kali并进行磁盘分区-2024.9最新
  • 解决:Vue 中 debugger 不生效
  • Mac笔记本上查看/user/目录下的文件的几种方法
  • 工程师 - ACPI和ACPICA的区别
  • 一文快速上手-create-vue脚手架
  • 笔记整理—内核!启动!—kernel部分(7)rcs文件和登录部分与密码解析
  • 朴素贝叶斯 (Naive Bayes)
  • 高德2.0 多边形覆盖物无法选中编辑
  • 时序最佳入门代码|基于pytorch的LSTM天气预测及数据分析
  • 85-MySQL怎么判断要不要加索引
  • 车载软件架构 --- SOA设计与应用(中)
  • MATLAB求解微分方程和微分方程组的详细分析
  • Sybase「退役」在即,某公共卫生机构如何实现 SAP Sybase 到 PostgreSQL 的持续、无缝数据迁移?
  • 如何通过Chrome浏览器轻松获取视频网站的TS文件
  • Linux下进程间的通信--共享内存
  • Big Data 流处理框架 Flink
  • 校园水电费管理微信小程序的设计与实现+ssm(lw+演示+源码+运行)
  • HashMap线程不安全|Hashtable|ConcurrentHashMap