【unity实战】使用unity的新输入系统InputSystem+有限状态机设计一个玩家状态机控制——实现玩家的待机 移动 闪避 连击 受击 死亡状态切换

最终效果

在这里插入图片描述

文章目录

  • 最终效果
  • 前言
  • 人物素材
  • 新输入系统InputSystem的配置
  • 动画配置
  • 代码文件路径
  • 状态机脚本
  • 创建玩家不同的状态脚本
  • 玩家控制
  • 源码
  • 完结

前言

前面我们已经写过了使用有限状态机制作一个敌人AI:【unity实战】在Unity中使用有限状态机制作一个敌人AI

那么玩家的状态机要怎么做呢?目前网上这一块内容也很少,当我们对人物的操作越来越多时,有限状态机技术可以很好的帮我们将各部分功能拆开,单独配置逻辑,代码更加优雅,接下来我们就用新输入系统InputSystem设计一个玩家状态机控制系统,其实跟之前的敌人有限状态机类似。

人物素材

https://bdragon1727.itch.io/16x16-pixel-adventures-character
在这里插入图片描述

新输入系统InputSystem的配置

新输入系统还不会使用的可以参考这篇文章:【推荐100个unity插件之18】Unity 新版输入系统InputSystem的基础使用

其实就是默认的配置加了攻击和闪避的操作
在这里插入图片描述

动画配置

动画基础知识:【Unity游戏开发教程】零基础带你从小白到超神27——混合状态,混合动画,动画分类

除了攻击动画,其他的都放在第一层
在这里插入图片描述
闪避动画我是通过不断修改玩家图片的FlipY值实现的
在这里插入图片描述

重点讲讲攻击动画连击
两个参数控制进入,isMeleeAttack主要是有效防止播放最后一段连击后,再播放一次第一段攻击
在这里插入图片描述
如果动画播放90%再次按下就会进入下一段攻击
在这里插入图片描述
所有动画播放为1,即播放完时退出
在这里插入图片描述

代码文件路径

在这里插入图片描述

状态机脚本

定义状态类型枚举

// 定义状态类型枚举
public enum StateType
{Idle, //待机Move, //移动Dodge, //闪避MeleeAttack, //近战攻击Hit, //受击Death //死亡
}

抽象基类,定义了所有状态类的基本结构

//抽象基类,定义了所有状态类的基本结构public abstract class IState
{protected FSM manager;// 当前状态机protected Parameter parameter;// 参数public abstract void OnEnter();// 进入状态时的方法public abstract void OnUpdate();// 更新方法public abstract void OnFixedUpdate();// 固定更新方法public abstract void OnExit();// 退出状态时的方法
}

可序列化的参数类,存储了角色的各种状态参数和配置

// 可序列化的参数类,存储了角色的各种状态参数和配置
using System;
using UnityEngine;[Serializable]
public class Parameter
{[Header("属性")]public float health; // TODO:生命值 仅仅用于测试,实际生命值可能并不放在这里[HideInInspector] public Animator animator;       // 角色动画控制器[HideInInspector] public AnimatorStateInfo animatorStateInfo;    // 动画状态信息[HideInInspector] public SpriteRenderer sr; // 精灵渲染器[HideInInspector] public Rigidbody2D rb; // 刚体[HideInInspector] public PlayerSystem inputSystem;//新的输入系统[Header("移动")]public float normalSpeed = 3f; // 默认移动速度public float attackSpeed = 1f; // 攻击时的移动速度[HideInInspector] public Vector2 inputDirection; // 输入的移动方向[HideInInspector] public float currentSpeed; // 当前移动速度[Header("攻击")]public float meleeAttackDamage; // 近战攻击造成的伤害[HideInInspector] public bool isMeleeAttack; // 是否进行近战攻击[Header("闪避")]public float dodgeForce; // 闪避的力量public float dodgeCooldown = 2f; // 闪避的冷却时间[HideInInspector] public bool isDodging = false; // 是否在闪避中[HideInInspector] public bool isDodgeOnCooldown = false; // 是否在闪避冷却中[Header("受伤与死亡")][HideInInspector] public bool isHurt; // 是否受伤[HideInInspector] public bool isDead; // 是否死亡[HideInInspector] public bool getHit;             // 是否被击中
}

新增玩家状态机

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;// 玩家有限状态机类
public class FSM : MonoBehaviour
{private IState currentState;        // 当前状态接口protected Dictionary<StateType, IState> states = new Dictionary<StateType, IState>();  // 状态字典,存储各种状态public Parameter parameter;  // 状态机参数public virtual void Awake(){parameter.rb = GetComponent<Rigidbody2D>();parameter.animator = GetComponent<Animator>();parameter.sr = GetComponent<SpriteRenderer>();// 初始化各个状态,并添加到状态字典中states.Add(StateType.Idle, new IdleState(this));states.Add(StateType.Move, new MoveState(this));states.Add(StateType.Dodge, new DodgeState(this));states.Add(StateType.MeleeAttack, new MeleeAttackState(this));states.Add(StateType.Hit, new HitState(this));states.Add(StateType.Death, new DeathState(this));TransitionState(StateType.Idle);    // 初始状态为Idle}public virtual void OnEnable(){currentState.OnEnter();}public virtual void Update(){//有效防止播放最后一段连击后,再播放一次第一段攻击parameter.animator.SetBool("isMeleeAttack", parameter.isMeleeAttack);parameter.animatorStateInfo = parameter.animator.GetCurrentAnimatorStateInfo(0);// 获取当前动画状态信息currentState.OnUpdate();}public virtual void FixedUpdate(){currentState.OnFixedUpdate();}// 状态转换方法public void TransitionState(StateType type){if (currentState != null)currentState.OnExit();// 先调用退出方法currentState = states[type];    // 更新当前状态为指定类型的状态currentState.OnEnter();         // 调用新状态的进入方法}// 切换操作映射public void SwitchActionMap(InputActionMap actionMap){parameter.inputSystem.Disable(); // 禁用当前的输入映射actionMap.Enable(); // 启用新的输入映射}public void Move(){// 根据当前状态设置角色速度parameter.currentSpeed = parameter.isMeleeAttack ? parameter.attackSpeed : parameter.normalSpeed;// 设置角色刚体的速度为移动方向乘以当前速度parameter.rb.velocity = parameter.inputDirection * parameter.currentSpeed;FlipTo();}// 翻转角色public void FlipTo(){if (parameter.inputDirection.x < 0){transform.localScale = new Vector3(-1, 1, 1);}if (parameter.inputDirection.x > 0){transform.localScale = new Vector3(1, 1, 1);}}// 开始闪避技能冷却的协程public void DodgeOnCooldown(){StartCoroutine(nameof(DodgeOnCooldownCoroutine));}public IEnumerator DodgeOnCooldownCoroutine(){yield return new WaitForSeconds(parameter.dodgeCooldown);// 等待闪避冷却时间parameter.isDodgeOnCooldown = false;// 闪避技能冷却结束,设置为不在冷却状态}
}

创建玩家不同的状态脚本

在这里插入图片描述
待机状态

using UnityEngine;
/// <summary>
/// 待机状态
/// </summary>public class IdleState : IState
{public IdleState(FSM manager){this.manager = manager;this.parameter = manager.parameter;}public override void OnEnter(){parameter.animator.Play("Idle");}public override void OnUpdate(){// parameter.anim.SetFloat("speed", parameter.rb.velocity.magnitude);// 如果受伤if (parameter.isHurt){manager.TransitionState(StateType.Hit);}//如果输入的移动方向不为0if (parameter.inputDirection != Vector2.zero){manager.TransitionState(StateType.Move);}//闪避if (parameter.isDodging){manager.TransitionState(StateType.Dodge);}//真正近战攻击if (parameter.isMeleeAttack){manager.TransitionState(StateType.MeleeAttack);}}public override void OnFixedUpdate() { }public override void OnExit() { }
}

移动状态


/// <summary>
/// 移动状态
/// </summary>
public class MoveState : IState
{public MoveState(FSM manager){this.manager = manager;this.parameter = manager.parameter;}public override  void OnEnter(){parameter.animator.Play("Run");}public override  void OnUpdate(){// 如果想按速度切换移动或奔跑动画// parameter.animator.SetFloat("speed", player.rb.velocity.magnitude);//受伤if (parameter.isHurt){manager.TransitionState(StateType.Hit);}//速度为0if (parameter.rb.velocity.magnitude < 0.01f){manager.TransitionState(StateType.Idle);}//闪避if (parameter.isDodging){manager.TransitionState(StateType.Dodge);}//近战攻击if (parameter.isMeleeAttack){manager.TransitionState(StateType.MeleeAttack);}}public override  void OnFixedUpdate(){manager.Move();}public override  void OnExit() { }
}

近战攻击状态

/// <summary>
/// 近战攻击状态
/// </summary>
public class MeleeAttackState : IState
{public MeleeAttackState(FSM manager){this.manager = manager;this.parameter = manager.parameter;}public override  void OnEnter(){parameter.animator.SetTrigger("MeleeAttack");}public override  void OnUpdate(){//受伤if (parameter.isHurt){manager.TransitionState(StateType.Hit);}// 动画播放95%切换到待机状态if (parameter.animatorStateInfo.normalizedTime >= .95f){manager.TransitionState(StateType.Idle);}}public override  void OnFixedUpdate(){manager.Move();}public override  void OnExit(){parameter.isMeleeAttack = false;}
}

闪避状态

using System.Collections;
using UnityEngine;
/// <summary>
/// 闪避状态
/// </summary>
public class DodgeState : IState
{public DodgeState(FSM manager){this.manager = manager;this.parameter = manager.parameter;}public override void OnEnter(){parameter.animator.Play("Dodge");}public override void OnUpdate(){//受击if (parameter.isHurt){manager.TransitionState(StateType.Hit);}//动画播放95%切换到待机状态if (parameter.animatorStateInfo.normalizedTime >= .95f){manager.TransitionState(StateType.Idle);}}public override void OnFixedUpdate(){manager.Move();if(parameter.isDodging) Dodge();}public override void OnExit() {parameter.isDodging = false;}// 进行闪避操作的方法public void Dodge(){// 施加闪避力量,根据输入方向和设定的闪避力量parameter.rb.AddForce(parameter.inputDirection * parameter.dodgeForce, ForceMode2D.Impulse);parameter.isDodgeOnCooldown = true;manager.DodgeOnCooldown();// 开始闪避冷却}
}

受击状态

/// <summary>
/// 受击状态
/// </summary>
public class HitState : IState
{public HitState(FSM manager){this.manager = manager;this.parameter = manager.parameter;}public override void OnEnter(){parameter.animator.Play("Hit");//TODO:仅用于测试parameter.health --;    // 减少角色生命值}public override  void OnUpdate(){//TODO:仅用于测试,如果角色生命值小于等于0,转换到死亡状态if (parameter.health <= 0){manager.TransitionState(StateType.Death);}//动画播放95%切换到待机状态if (parameter.animatorStateInfo.normalizedTime >= .95f){manager.TransitionState(StateType.Idle);}}public override  void OnFixedUpdate(){manager.Move();}public override  void OnExit(){parameter.isHurt = false;}
}

死亡状态

/// <summary>
/// 死亡状态
/// </summary>
public class DeathState : IState
{public DeathState(FSM manager){this.manager = manager;this.parameter = manager.parameter;}public override void OnEnter(){parameter.isDead = true;manager.SwitchActionMap(parameter.inputSystem.UI);//切换为UI输入parameter.animator.Play("Dead");}public override void OnUpdate() { }public override void OnFixedUpdate() { }public override void OnExit() { }
}

玩家控制

新增PlayerController,获取玩家的输入

using UnityEngine;
using UnityEngine.InputSystem;public class PlayerController : FSM {public override void Awake() {base.Awake();// 初始化输入控制parameter.inputSystem = new PlayerSystem();parameter.inputSystem.Player.Move.performed += Move;parameter.inputSystem.Player.Move.canceled += StopMove;parameter.inputSystem.Player.Dodge.started += Dodge;parameter.inputSystem.Player.MeleeAttack.started += MeleeAttack;SwitchActionMap(parameter.inputSystem.Player); // 切换到游戏操作的输入映射}void OnDisable(){// 禁用所有输入parameter.inputSystem.Disable();}public override void Update(){//TODO:用于测试 如果按下回车键,设置被击中状态为trueif (Input.GetKeyDown(KeyCode.Return)){// PlayerHurt();parameter.isHurt = true;}base.Update();}public void Move(InputAction.CallbackContext context){parameter.inputDirection = parameter.inputSystem.Player.Move.ReadValue<Vector2>();   }// 停止移动,将输入方向设为零向量public void StopMove(InputAction.CallbackContext context){parameter.inputDirection = Vector2.zero;}// 触发闪避的方法public void Dodge(InputAction.CallbackContext context){//如果当前不在冷却中,则开始闪避if(!parameter.isDodgeOnCooldown) parameter.isDodging = true;}// 近战攻击public void MeleeAttack(InputAction.CallbackContext context){parameter.isMeleeAttack = true;}
}

效果
在这里插入图片描述

源码

整理好了我会放上来

完结

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,以便我第一时间收到反馈,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!

好了,我是向宇,https://xiangyu.blog.csdn.net

一位在小公司默默奋斗的开发者,出于兴趣爱好,最近开始自学unity,闲暇之余,边学习边记录分享,站在巨人的肩膀上,通过学习前辈们的经验总是会给我很多帮助和启发!php是工作,unity是生活!如果你遇到任何问题,也欢迎你评论私信找我, 虽然有些问题我也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~
在这里插入图片描述

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

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

相关文章

2024华为OD机试真题-找数字-(C++/Python)-C卷D卷-200分

2024华为OD机试题库-(C卷+D卷)-(JAVA、Python、C++) 题目描述 小扇和小船今天又玩起来了数字游戏, 小船给小扇一个正整数 n(1 ≤ n ≤ 1e9),小扇需要找到一个比 n 大的数字 m,使得 m 和 n 对应的二进制中 1 的个数要相同,如: 4对应二进制1008对应二进制1000其中1的个数…

【苍穹外卖】Day1遇到的问题

1、lombok版本不兼容问题 java: java.lang.IllegalAccessError: class lombok.javac.apt.LombokProcessor (in unnamed module 0x3278991b) cannot access class com.sun.tools.javac.processing.JavacProcessingEnvironment (in module jdk.compiler) because module jdk.comp…

Java项目:基于SSM框架实现的毕业论文管理系统【ssm+B/S架构+源码+数据库+毕业论文】

一、项目简介 本项目是一套基于SSM框架实现的毕业论文管理系统 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&#xff0c;eclipse或者idea 确保可以运行&#xff01; 该系统功能完善、界面美观、操作简单、功能…

javaScript(九) 数组

] console.log(af.pop()) console.log(af) 第一个输出&#xff1a;{id:2,name:“枷”,score:“98”} 第二个输出&#xff1a;[ {id:1,name:“My”,score:“90”}, {id:3,name:“123”,score:“80”} ] Array.prototype.shift() 删除数组中的第一个元素&#xff0c;该方法…

一个项目学习Vue3---Vue计算属性

观察下面一段代码&#xff0c;学习Vue计算属性 <template><div><span>用户大于10岁的数量&#xff1a;{{ userVue.filter(user>user.age>10).length}}</span><span>用户大于10岁的数量2&#xff1a;{{ userAgeltTen}}</span><sp…

基于轨迹信息的图像近距离可行驶区域方案验证

一 图像可行驶区域方案 1.1 标定场景 1.2 标定步骤 设计一定间距标定场&#xff0c;在标定场固定位置设置摄像头标定标识点。主车开到标定场固定位置录制主车在该位置各个摄像头数据&#xff0c;通过摄像头捕获图像获取图像上关键点坐标pts-2d基于标定场设计&#xff0c;计算…

保函到期提醒是银行或金融机构提供的一项服务,旨在确保客户及时了解保函即将到期的情况,从而避免因保函过期而导致的风险或违约责任。

保函到期提醒是银行或金融机构提供的一项服务&#xff0c;旨在确保客户及时了解保函即将到期的情况&#xff0c;从而避免因保函过期而导致的风险或违约责任。以下是保函到期提醒的一些关键方面&#xff1a; 1. **保函定义**&#xff1a; - 保函是一种由银行出具的书面承诺&…

vue实现左右拖动分屏

效果图如下&#xff1a; 封装组件 <template><div ref"container" class"container"><div class"left-content" :style"leftStyle">/**定义左侧插槽**/<slot name"left"></slot></div>…

Springboot+Vue3开发学习笔记《2》

SpringbootVue3开发学习笔记《2》 博主正在学习SpringbootVue3开发&#xff0c;希望记录自己学习过程同时与广大网友共同学习讨论。 总共涉及两部分&#xff0c;第一部分为基础部分学习&#xff0c;第二部分为实战部分。 一、学习路径 1.1 基础部分 配置文件整合MyBatisBea…

中英双语介绍美国的州:阿拉斯加州(Alaska)

中文版 阿拉斯加州&#xff08;Alaska&#xff09;位于美国西北角&#xff0c;是美国面积最大的州&#xff0c;以其壮丽的自然景观、丰富的矿产资源和独特的野生动物闻名。以下是对阿拉斯加州的详细介绍&#xff0c;包括其地理位置、人口、经济、教育、文化和主要城市。 地理…

QQ聊天记录删除了怎么恢复?这4个方法让你秒找回!

在现代社会&#xff0c;QQ已经成为我们日常交流和工作中不可或缺的沟通工具。然而&#xff0c;有时我们可能会不小心删除了重要的聊天记录&#xff0c;这会带来诸多不便甚至困扰。那么&#xff0c;当你发现自己误删了数据&#xff0c;qq聊天记录删除了怎么恢复呢&#xff1f;有…

第14届蓝桥杯Python青少组中/高级组选拔赛(STEMA)2022年8月21日真题

第14届蓝桥杯Python青少组中/高级组选拔赛&#xff08;STEMA&#xff09;2022年8月21日真题 题目总数&#xff1a;5 总分数&#xff1a;128 更多真题下载点我&#x1f446; 编程题 第 1 题 问答题 编程实现&#xff1a; 给定一个正整数&#xff0c;输出正整数个位上的…

tsconfig.json的include和exclude作用

tsconfig.json中的include和exclude属性用于指定需要被编译的TypeScript文件和需要被排除的文件。‌ include属性&#xff1a;‌用于指定哪些.ts、‌.tsx或.d.ts文件需要被编译。‌如果不指定include属性&#xff0c;‌则默认当前目录下除了exclude之外的所有.ts、‌.d.ts、‌…

昇思25天学习打卡营第11天|LSTM+CRF序列标注

序列标注指给定输入序列&#xff0c;给序列中每个Token进行标注标签的过程。序列标注问题通常用于从文本中进行信息抽取&#xff0c;包括分词(Word Segmentation)、词性标注(Position Tagging)、命名实体识别(Named Entity Recognition, NER)等。 和人理解语言一样&#xff0c…

2024-07-04 base SAS programming学习笔记8(HTML)

当使用ODS来进行结果或数据集输出的时候&#xff0c;可以同时设置多个ODS 命令&#xff0c;同时输出到多个不同的文件。使用_ALL_ 表示关闭所有的ODS输出窗口&#xff0c;比如&#xff1a; ods html file(body)"html-file-pathname"; ods html file"pdf-file-pa…

【C#】如何在窗体程序中调用多行CMD命令

【背景】 用VS写一个C#窗体程序&#xff0c;第一步需要用CMD启动一个外部服务并发送信息给该服务器&#xff0c;涉及两步命令&#xff0c;第一步是启动服务&#xff0c;第二步是发送信息。 【分析】 要点&#xff1a; 如何指定启动CMD的路径在服务exe所在路径下&#xff1b…

中国东方资产管理25届秋招北森测评笔试如何高分通过?真题考点分析看完这篇就够了

一、东方资管校招测评题型分析 中国东方资产管理股份有限公司&#xff08;中国东方资管&#xff09;的校园招聘测评题型主要包括以下几个部分&#xff1a; 1. **计分题&#xff0c;行测知识**&#xff1a;这部分题量大约在56-57题左右&#xff0c;分为不同的模块进行计时测试。…

Spzhi知识付费社区主题免费下载

主题介绍 用typecho打造一款知识付费社区主题&#xff0c;带会员功能&#xff0c;为内容创业者提供知识变现一站式解决方案&#xff0c;让用户沉淀到自己的平台&#xff0c;形成自己的私域流量池&#xff0c;打造流量闭环&#xff0c;零门槛搭建你的移动网络课堂 主题功能 支…

SpringBoot Task 定时任务

springboot中使用Task定时任务非常简单 springboot 中自带的都有注解不需要引入依赖 第一步&#xff1a;在启动类上添加启用定时任务注解 EnableScheduling //开启任务调度 第二步&#xff1a;创建一个springboot组件用于定时任务管理 package cn.lsy.api.Task;import cn.ls…