Unity复刻胡闹厨房复盘 模块一 新输入系统订阅链与重绑定

        本文仅作学习交流,不做任何商业用途

        郑重感谢siki老师的汉化教程与代码猴的免费教程以及搬运烤肉的小伙伴

                                                          版本:Unity6                      

                                                          模板:3D 核心                      

                                                          渲染管线:URP 

    ------------------------------------------------------------------------------------------------------------------------------

    在此之前你应该对新输入系统有一定的了解,关于新输入系统的用法与解释:Unity新输入系统 之 InputAction(输入配置文件最基本的单位)_unity inputaction-CSDN博客

关于新输入系统的简单实战:Unity 新输入系统实战 控制2D角色移动动画(俯视)-CSDN博客

目录

实现功能与逻辑拆解

1.获取WASD的基础输入​编辑

2.自定义操作按键

3.按键重绑

4 .重绑定后的保存与读取

全局概览


        我将输入管理类命名为GameInput 其创建时候就写为了单例模式,因为但凡是拥有全局唯一实例的类 或者 是该类的生命周期占据了整个场景 就应该写为单例模式 

实现功能与逻辑拆解

1.获取WASD的基础输入

对于新输入系统的创建与生成C#文件我便不再赘述,Unity6自带

你可以在InputAction看到其已经定义好了很多内容

         首先声明输入系统的C#类 然后开启

action = new InputSystem_Actions();action.Enable();

        直接读取输入值 可以看到Move这一Action是Value的动作类型以及Vector2的控制类型

        因此代码就可以直接这么写: 

   public Vector3 GetInputKeyboard() {Vector2 direcation = action.Player.Move.ReadValue<Vector2>();//float x = Input.GetAxisRaw("Horizontal");//float z = Input.GetAxisRaw("Vertical");Vector3 move = new Vector3(direcation.x, 0, direcation.y);move = move.normalized;return move;}

至于为什么返回单位化后的向量可以看这一篇,其并不是本文的重点 :Unity中的数学应用 之 角色移动中单位化向量的妙用 (小学难度)-CSDN博客

2.自定义操作按键

        这里有个前置知识点:发布者-订阅模式的特点就是发布者发布事件与调用 ,订阅者只需要订阅上就完事了

         对于此部分可以自定义Action为Button类型

        对于具体按键可以勾选分类: 

        代码是这么写的:

    private static GameInput instance;public static GameInput Instance => instance;private InputSystem_Actions action;public EventHandler interact;public EventHandler operater;public EventHandler pause;private void Awake() {if (instance == null) {instance = this;}else {Destroy(instance);}action = new InputSystem_Actions();if (PlayerPrefs.HasKey(PLAYERBDINGINFO))action.LoadBindingOverridesFromJson(PlayerPrefs.GetString(PLAYERBDINGINFO));action.Enable();//触发订阅—Eaction.Player.Interact.started += Interact_started;//触发订阅-Faction.Player.Operater.started += Operater_started;//触发订阅-ESCaction.Player.Pause.started += Pause_started;}private void OnDestroy() {action.Player.Interact.started -= Interact_started;action.Player.Operater.started -= Operater_started;action.Player.Pause.started -= Pause_started;action.Dispose();}private void Pause_started(UnityEngine.InputSystem.InputAction.CallbackContext obj) {pause?.Invoke(this, EventArgs.Empty);}private void Operater_started(UnityEngine.InputSystem.InputAction.CallbackContext obj) {operater?.Invoke(this, EventArgs.Empty);}private void Interact_started(UnityEngine.InputSystem.InputAction.CallbackContext obj) {interact?.Invoke(this, EventArgs.Empty);}

        解释:下面部分是对按键手势(Action)的订阅

        action.Enable();//触发订阅—Eaction.Player.Interact.started += Interact_started;//触发订阅-Faction.Player.Operater.started += Operater_started;//触发订阅-ESCaction.Player.Pause.started += Pause_started;

        订阅的谁呢?是如下三个函数 注意其参数都是自动生成的

 private void Pause_started(UnityEngine.InputSystem.InputAction.CallbackContext obj) {pause?.Invoke(this, EventArgs.Empty);}private void Operater_started(UnityEngine.InputSystem.InputAction.CallbackContext obj) {operater?.Invoke(this, EventArgs.Empty);}private void Interact_started(UnityEngine.InputSystem.InputAction.CallbackContext obj) {interact?.Invoke(this, EventArgs.Empty);}

        而三个函数内又包裹了一层C#提供可传参委托

       其参数sender代表事件的发布者 也就是GameIput类本身

      e是EventArgs类型或者派生自EventArgs的类型,通常用于传递和事件相关的信息,这里传值为EventArgs.Empty 也就是空

    也就是他们三个

public EventHandler interact;public EventHandler operater;public EventHandler pause;

         所以对于他们三个的调用也就嵌在了对新输入系统手势的订阅上

        那么谁去订阅呢?Player 你会发现Player这里又是一层封装?但不是委托与事件 

        BaseCounter 是柜台基类 我们以后会讲,curCounter用于存储当前玩家获得的柜台

       private BaseCounter curCounter;void Start() {//Application.targetFrameRate = 60;this.HoldPoints = transform.Find("PlayerHoldPoint");GameInput.Instance.interact += OnInterAction;GameInput.Instance.operater += OnOperaterAction;}private void OnInterAction(object sender, EventArgs s) {curCounter?.Interact(this);}private void OnOperaterAction(object sender, EventArgs e) {curCounter?.InteractOperater(this);}

        Interact与InteractOperater的定义在柜台类基类之中是两个虚方法,由子类去实现

using UnityEngine;public class BaseCounter : FoodObjcetHolder {[SerializeField] protected Transform SelectPrefab;public void SelectPrefabSecureAssign(string name) {if (SelectPrefab == null) {SelectPrefab = transform.Find(name);}}public virtual void Interact(Player player) {Debug.Log("未对父类进行重写");}public virtual void InteractOperater(Player player) {}public void CounterSelect() {SelectPrefab.gameObject?.SetActive(true);}public void CounterUnSelect() {SelectPrefab.gameObject?.SetActive(false);}}

         子类实现我们不去考虑,目前对于一个按键的订阅链我们已经整理完了

        由下图所示

   

        你要说这不是脱了裤子放p吗?其实不然 其道理在于

        如果只是GameInput类自我消化 只需要第一条黑线 但是GameInput类要与Player类进行交互 而事件的发布订阅解决了这个问题,所以有了第二条黑线

        Player也不负责执行柜台的逻辑 所以就需要第三条黑线

3.按键重绑

        这个的原理如下

1.获取对应的Action下的按键

action.Player.Move;action.Player.Interact;action.Player.Operater;action.Player.Pause;

       2.通过对应的索引去得到对应值键 也就是下图
         

        在回调函数中执行其他办法,注意最后那个Start()方法一定要开启不然没有任何反应

     actionKey.PerformInteractiveRebinding(index).OnComplete((callback) => {Debug.Log("重新绑定完成");action.Player.Enable();SettingUI.Instance.UpdateUI();SettingUI.Instance.HideBindingInfo();PlayerPrefs.SetString(PLAYERBDINGINFO,action.SaveBindingOverridesAsJson());}).Start();

        先别管actionKey,也别管函数块内部做了什么,重点下面这个API是重绑定的关键:

PerformInteractiveRebinding(index).OnComplete

        做了一个枚举去得到所有不同的输入

public enum E_BindingKey{ w,a,s,d,e,f,esc
}

        重新绑定只需要传入一个枚举值,就可以

  public void ReBinding(E_BindingKey e_BindingKey){Debug.Log("进入重新绑定");action.Player.Disable(); InputAction actionKey = null;int index = -1;switch (e_BindingKey) {case E_BindingKey.w:index = 2;actionKey = action.Player.Move;break;case E_BindingKey.a:index = 6;actionKey = action.Player.Move;break;case E_BindingKey.s:index = 4;actionKey = action.Player.Move;break;case E_BindingKey.d:index = 8;actionKey = action.Player.Move;break;case E_BindingKey.e:index = 0;actionKey = action.Player.Interact;break;case E_BindingKey.f:index = 0;actionKey = action.Player.Operater;break;case E_BindingKey.esc:index = 0;actionKey = action.Player.Pause;break;default:break;}if(actionKey != null) {actionKey.PerformInteractiveRebinding(index).OnComplete((callback) => {Debug.Log("重新绑定完成");action.Player.Enable();SettingUI.Instance.UpdateUI();SettingUI.Instance.HideBindingInfo();PlayerPrefs.SetString(PLAYERBDINGINFO,action.SaveBindingOverridesAsJson());}).Start();}else{Debug.Log("actionKey为空");}//actionKey.Dispose();}

4 .重绑定后的保存与读取

        重新加载会将场景所有类的内存回收,但是存在本地的可以持久化,因此无论是何种持久化方式都可以通过如下两个API去存取成json字符串

        这里用PlayerPrefs演示

        写入:

PlayerPrefs.SetString(PLAYERBDINGINFO,action.SaveBindingOverridesAsJson());

        读取: 

   action.LoadBindingOverridesFromJson(PlayerPrefs.GetString(PLAYERBDINGINFO));

        至于放在哪里 不用我多说了相信你对unity的生命周期并不陌生 

全局概览

using System;
using UnityEngine;
using UnityEngine.InputSystem;
public enum E_BindingKey{ w,a,s,d,e,f,esc
}
public class GameInput : MonoBehaviour {private const string PLAYERBDINGINFO = "PLAYERBDINGINFO";private static GameInput instance;public static GameInput Instance => instance;private InputSystem_Actions action;public EventHandler interact;public EventHandler operater;public EventHandler pause;private void Awake() {if (instance == null) {instance = this;}else {Destroy(instance);}action = new InputSystem_Actions();if (PlayerPrefs.HasKey(PLAYERBDINGINFO))action.LoadBindingOverridesFromJson(PlayerPrefs.GetString(PLAYERBDINGINFO));action.Enable();//触发订阅—Eaction.Player.Interact.started += Interact_started;//触发订阅-Faction.Player.Operater.started += Operater_started;//触发订阅-ESCaction.Player.Pause.started += Pause_started;}private void OnDestroy() {action.Player.Interact.started -= Interact_started;action.Player.Operater.started -= Operater_started;action.Player.Pause.started -= Pause_started;action.Dispose();}private void Pause_started(UnityEngine.InputSystem.InputAction.CallbackContext obj) {pause?.Invoke(this, EventArgs.Empty);}private void Operater_started(UnityEngine.InputSystem.InputAction.CallbackContext obj) {operater?.Invoke(this, EventArgs.Empty);}private void Interact_started(UnityEngine.InputSystem.InputAction.CallbackContext obj) {interact?.Invoke(this, EventArgs.Empty);}/// <summary>/// 读取输入/// </summary>/// <returns>移动朝向</returns>public Vector3 GetInputKeyboard() {Vector2 direcation = action.Player.Move.ReadValue<Vector2>();//float x = Input.GetAxisRaw("Horizontal");//float z = Input.GetAxisRaw("Vertical");Vector3 move = new Vector3(direcation.x, 0, direcation.y);move = move.normalized;return move;}public void ReBinding(E_BindingKey e_BindingKey){Debug.Log("进入重新绑定");action.Player.Disable(); InputAction actionKey = null;int index = -1;switch (e_BindingKey) {case E_BindingKey.w:index = 2;actionKey = action.Player.Move;break;case E_BindingKey.a:index = 6;actionKey = action.Player.Move;break;case E_BindingKey.s:index = 4;actionKey = action.Player.Move;break;case E_BindingKey.d:index = 8;actionKey = action.Player.Move;break;case E_BindingKey.e:index = 0;actionKey = action.Player.Interact;break;case E_BindingKey.f:index = 0;actionKey = action.Player.Operater;break;case E_BindingKey.esc:index = 0;actionKey = action.Player.Pause;break;default:break;}if(actionKey != null) {actionKey.PerformInteractiveRebinding(index).OnComplete((callback) => {Debug.Log("重新绑定完成");action.Player.Enable();SettingUI.Instance.UpdateUI();SettingUI.Instance.HideBindingInfo();PlayerPrefs.SetString(PLAYERBDINGINFO,action.SaveBindingOverridesAsJson());}).Start();}else{Debug.Log("actionKey为空");}//actionKey.Dispose();}public string GetBindingKey(E_BindingKey e_BindingKey){switch (e_BindingKey) {case E_BindingKey.w:return action.Player.Move.bindings[2].ToDisplayString();case E_BindingKey.a:return action.Player.Move.bindings[6].ToDisplayString();case E_BindingKey.s:return action.Player.Move.bindings[4].ToDisplayString();case E_BindingKey.d:return action.Player.Move.bindings[8].ToDisplayString();case E_BindingKey.e:return action.Player.Interact.bindings[0].ToDisplayString();case E_BindingKey.f:return action.Player.Operater.bindings[0].ToDisplayString();case E_BindingKey.esc:return action.Player.Pause.bindings[0].ToDisplayString();default:return "";}}
}

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

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

相关文章

从零开始的编程-java篇1.6.1 万变不离其宗,hello word

前言&#xff1a; 通过实践而发现真理&#xff0c;又通过实践而证实真理和发展真理。从感性认识而能动地发展到理性认识&#xff0c;又从理性认识而能动地指导革命实践&#xff0c;改造主观世界和客观世界。实践、认识、再实践、再认识&#xff0c;这种形式&#xff0c;循环往…

【漏洞复现】CVE-2021-45788 SQL Injection

漏洞信息 NVD - cve-2021-45788 Time-based SQL Injection vulnerabilities were found in Metersphere v1.15.4 via the “orders” parameter. Authenticated users can control the parameters in the “order by” statement, which causing SQL injection. API: /test…

Mac系统下 idea运行maven项目中存在的问题BeanDefinitionStoreException

1.在进行 注解XML 方式整合三层架构事出现此问题 org.springframework.beans.factory.BeanDefinitionStoreException: Failed to read candidate component class: file [/Volumes/PS2000/Java/SpringProject/micro-shop/spring-annotation-practice-03/target/classes/com/ja…

循环和迭代

从更高层次的思维角度来看迭代和循环的区别&#xff1a; 哲学层面&#xff1a; 迭代体现了"螺旋上升"的发展理念&#xff0c;每次迭代都在前一次的基础上有所提升和改进 循环体现了"周而复始"的概念&#xff0c;强调重复相同的过程 思维方式&#xff1a…

shiro注入filter内存马(绕过长度限制)

shiro环境 https://github.com/yyhuni/shiroMemshell&#xff08;实验环境&#xff09; 这里用的 Client_memshell.java package com.example.demo;import javassist.ClassPool; import javassist.CtClass; import org.apache.shiro.crypto.AesCipherService; import org.ap…

c语言大一期末复习

l例1&#xff1a;输入一行字符&#xff0c;统计其中空格的个数 #include<stdio.h> int main( ) { char ch; int count0; while((chgetchar())!\n) { if(ch ) { count1; } } printf("%d\n",coun…

心血管疾病中医怎么调理

心血管疾病在中医范畴内属于胸痹&#xff0c;中医会根据不同的证候&#xff0c;如心血瘀阻证、寒凝心脉证、痰浊闭阻证、心肾阴虚证、气阴两虚证等&#xff0c;采取不同的调理方法。以下是一些中医调理心血管疾病的常用手段&#xff1a; 一、中药调理 ‌心血瘀阻证‌&#xf…

若依微服务如何获取用户登录信息

文章目录 1、需求提出2、应用场景3、解决思路4、注意事项5、完整代码第一步&#xff1a;后端获取当前用户信息第二步&#xff1a;前端获取当前用户信息 5、运行结果6、总结 1、需求提出 在微服务架构中&#xff0c;获取当前用户的登录信息是开发常见的需求。无论是后端处理业务…

.net core sdk 项目多版本切换

使用global.json文件指定项目要使用的sdk版本&#xff1a; 在项目根目录下执行cmd命令&#xff08;sdk的版本默认为当前使用的最新的sdk的版本&#xff09; 默认sdk&#xff1a;dotnet new globaljson指定sdk&#xff1a;dotnet new globaljson --sdk-version <version>…

群晖利用acme.sh自动申请证书并且自动重载证书的问题解决

前言 21年的时候写了一个在群晖&#xff08;黑群晖&#xff09;下利用acme.sh自动申请Let‘s Encrypt的脚本工具 群晖使用acme自动申请Let‘s Encrypt证书脚本&#xff0c;自动申请虽然解决了&#xff0c;但是自动重载一直是一个问题&#xff0c;本人也懒&#xff0c;一想到去…

raid 状态查看 storcli64

场景 当磁盘报错的时候使用该命令排查 fdisk -l /dev/sdb fdisk: cannot open /dev/sdb: Input/output error进一步使用 smartctl 排查 smartctl -a /dev/sdb 输出 smartctl 7.1 2019-12-30 r5022 [x86_64-linux-5.4.0-144-generic] (local build) Copyright (C) 2002-19, B…

《探索PyTorch计算机视觉:原理、应用与实践》

一、PyTorch 与计算机视觉的奇妙相遇 在当今数字化的时代&#xff0c;计算机视觉作为一门能够赋予机器 “看” 的能力的技术&#xff0c;正以前所未有的速度蓬勃发展&#xff0c;深刻地改变着我们的生活和众多行业的运作模式。从智能手机中的人脸识别解锁&#xff0c;到安防监控…

使用VSCode Debugger 调试 React项目

一般我们调试代码时&#xff0c;用的最多的应该就是console.log方式了&#xff0c;还有的是使用Chrome DevTools 通过在对应的 sourcemap代码位置打断点进行调试&#xff0c;除了上面两种方式外还有一种更好用的调试方式&#xff1a; VSCode Debugger。 VSCode Debugger可以直…

js 深度克隆

深度克隆&#xff08;Deep Clone&#xff09;是指复制一个对象或数组及其所有嵌套结构的副本&#xff0c;使得克隆后的对象与原对象完全独立。JavaScript 提供了一些方法实现深度克隆&#xff0c;但每种方法有其优缺点。 1. 常用方法 1.1 使用 JSON.parse 和 JSON.stringify …

mapbox基础,加载mapbox官方地图

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;mapbox 从入门到精通 文章目录 一、&#x1f340;前言1.1 ☘️mapboxgl.Map 地图对象…

汽车IVI中控开发入门及进阶(三十八):手机投屏HiCar开发

手机投屏轻松实现手机与汽车的无缝连接,导航、音乐、通话等功能应有尽有,还支持更多第三方应用,让车载互联生活更加丰富多彩。 HiCar在兼容性和开放性上更具优势。 手机投屏可以说是车机的杀手级应用,大大拓宽了车机的可用性范围。其中华为推出的HiCar就是非常好用的一种。…

优化程序中的数据:从数组到代数

前言 我们往往都希望优化我们的程序&#xff0c;使之达到一个更好的效果&#xff0c;程序优化的一个重点就是速度&#xff0c;加快速度的一个好办法就是使用并行技术&#xff0c;但是&#xff0c;并行时我们要考虑必须串行执行的任务&#xff0c;也就是有依赖关系的任务&#…

Elasticsearch:确保业务规则与语义搜索无缝协作

作者&#xff1a;来自 Elastic Kathleen DeRusso 利用查询规则与语义搜索和重新排序相结合的强大功能。 更多阅读&#xff1a; Elasticsearch 8.10 中引入查询规则 - query rules Elasticsearch 查询规则现已正式发布 - query rules 你是否知道查询规则&#xff08;query ru…

文本模式下成功。ubuntu P104成功。

https://www.nvidia.com/en-us/drivers/details/226760/ 你下载的 NVIDIA-Linux-x86_64-470.256.02.run 是 NVIDIA 提供的二进制驱动程序安装文件。以下是详细的步骤&#xff0c;帮助你正确安装这个驱动程序。 1. 备份现有驱动&#xff08;可选&#xff09; 在安装新驱动之前…

把riscv32位系统弄懂1:riscv32 CPU指令学习

Riscv手册 首先下载手册&#xff1a;文件下载----中国开放指令生态(RISC-V)联盟 从这个页面下载riscv-spec-v2.1中文版 也可以下载中科大的这本&#xff1a;RISC-V手册 Riscv32指令集包括基础指令集和一些扩展指令集&#xff0c;比如在ESP32C3技术手册中&#xff0c;写到E…