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();
        //触发订阅—E
        action.Player.Interact.started += Interact_started;
        //触发订阅-F
        action.Player.Operater.started += Operater_started;
        //触发订阅-ESC
        action.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();
        //触发订阅—E
        action.Player.Interact.started += Interact_started;
        //触发订阅-F
        action.Player.Operater.started += Operater_started;
        //触发订阅-ESC
        action.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();
        //触发订阅—E
        action.Player.Interact.started += Interact_started;
        //触发订阅-F
        action.Player.Operater.started += Operater_started;
        //触发订阅-ESC
        action.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 "";
        }

    }
}

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

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

相关文章

CentOS 7 安装、测试和部署FastDFS

目录 FastDFS环境搭建 安装 libfastcommon 库 安装FastDFS 查看编译后的文件 FastDFS配置 FastDFS启动 启动tracker服务 启动storage服务 查看storage是否已经注册到了tracker下 查看存储文件的目录 FastDFS重启 FastDFS关闭 使用fdfs_test进行测试 修改client.co…

通用导出任何对象列表数据的excel工具类

在工作中经常会遇到列表数据的导出&#xff0c;每次需要的时候都要去开发一次&#xff0c;且数据不断在变化&#xff0c;于是就有了下述的工具类&#xff0c;可传入各种实体对象的List&#xff0c;最终以指定格式导出excel&#xff0c;废话不多说&#xff0c;上代码~ 控制层代…

前端:改变鼠标点击物体的颜色

需求&#xff1a; 需要改变图片中某一物体的颜色&#xff0c;该物体是纯色&#xff1b; 鼠标点击哪个物体&#xff0c;哪个物体的颜色变为指定的颜色&#xff0c;利用canvas实现。 演示案例 代码Demo <!DOCTYPE html> <html lang"en"><head>&l…

AI口播数字人系统快速搭建方法来袭!零经验小白也能学会!

随着AI口播数字人的身影在短视频和直播中的出现频率持续升高&#xff0c;越来越多的创业者都察觉到了AI口播数字人系统所蕴含着的巨大潜在用户规模和广阔收益前景&#xff0c;并打听起了AI口播数字人系统怎么搭建相关的各种消息。 毕竟&#xff0c;根据当前的使用情况来看&…

中小学生心理健康测评系统:精准洞察,助力成长!

随着社会的发展&#xff0c;中小学生的心理健康问题日益受到关注。国家出台了一系列政策&#xff0c;强调要加强学生心理健康教育。然而&#xff0c;在实际的校园环境中&#xff0c;中小学生面临着各种各样的心理挑战&#xff0c;课程增多、难度加大&#xff0c;考试频繁&#…

2024年12月英语六级CET6写作与翻译笔记

目录 1 写作 1.1 大学为学生提供了探索各种可能性 1.2 自律在个人成长中的重要性 1.3 切实可行的目标 2 翻译 2.1 洋山港(Yangshan Port) 2.2 中国航天事业 2.3 北斗卫星导航系统 1 写作 1.1 大学为学生提供了探索各种可能性 1.2 自律在个人成长中的重要性 1.3 切实可…

Unity性能优化 --- 减少OverDraw

OverDraw(过度绘制)就是GPU多次重复绘制同一像素点的操作。在Unity 中渲染的图像由数百万个像素组成&#xff0c;如果这些像素被多次绘制&#xff0c;那么会造成GPU极大的性能损耗。例如下图多个物体叠加放在一起 注&#xff1a;棕色越深的地方&#xff0c;过度绘制的次数越多。…

PostgreSQL 的历史

title: PostgreSQL 的历史 date: 2024/12/23 updated: 2024/12/23 author: cmdragon excerpt: PostgreSQL 是一款功能强大且广泛使用的开源关系型数据库管理系统。其历史可以追溯到1986年,当时由加州大学伯克利分校的一个研究团队开发。文章将深入探讨 PostgreSQL 的起源、…

python学opencv|读取图像(二十一)使用cv2.circle()绘制圆形进阶

【1】引言 前序已经掌握了使用cv2.circle()绘制圆形的基本操作&#xff0c;相关链接为&#xff1a; python学opencv|读取图像&#xff08;二十&#xff09;使用cv2.circle()绘制圆形-CSDN博客 由于圆形本身绘制起来比较简单&#xff0c;因此可以自由操作的空间也就大&#x…

大数据-256 离线数仓 - Atlas 数据仓库元数据管理 正式安装 启动服务访问 Hive血缘关系导入

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; Java篇开始了&#xff01; 目前开始更新 MyBatis&#xff0c;一起深入浅出&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff0…

保险科技“数智化+”赋能险企高质量发展

文 / 太保科技有限公司人工智能服务事业群资深产品经理 娄昕盛 中国太平洋保险(集团)股份有限公司数智研究院人工智能首席专家 徐国强 中国太平洋保险(集团)股份有限公司数智研究院执行院长 王磊 近年来,保险科技正处在“数字化+”向“数智化+”发展的过渡阶段,…

AI科研助手开发总结:向量与数据权应用(二)

一、前言 继上篇文章&#xff1a;AI科研助手开发总结&#xff1a;向量与数据权限的应用&#xff08;一&#xff09; 本章根据向量库内存储数据及权限&#xff0c;向量库统一维护和管理数据权限方案讨论。 二、方案分析-基于向量Fields 2.1 思路 结合橙语AI科研助手的业务场…

数字逻辑(七)——逻辑运算中三种基本运算及其符合运算

目录 1 三种基本逻辑运算 1.1 与&#xff08;AND&#xff09; 1.2 或&#xff08;OR&#xff09; 1.3 非&#xff08;NOT&#xff09; 2 由基本门电路组成的其他门电路 2.1 异或 2.2. 同或 2.3 与非 2.4 或非 用于分析数字电路中逻辑功能的数学方法——逻辑代数&#…

分布式事务的解决方案(欢迎讨论~)

目录 背景 CAP定理 BASE理论 场景重现​编辑 分布式事务常见的解决分案 1.二段提交 2.三段提交 3.TCC模式 4.分布式补偿事务&#xff08;Saga&#xff09; 5.Seata分布式框架-XA模式 6.Seata分布式框架-AT模式 XA AT TCC SAGA 的对比 背景 首先必须介绍一下分布式中…

汽车IVI中控开发入门及进阶(43):NanoVG

NanoVG:基于OpenGL的轻量级抗锯齿2D矢量绘图库 NanoVG是一个跨平台、基于OpenGL的矢量图形渲染库。它非常轻量级,用C语言实现,代码不到5000行,非常精简地实现了一套HTML5 Canvas API,做为一个实用而有趣的工具集,用来构建可伸缩的用户界面和可视化效果。NanoVG-Library为…

从0到1实现一个RS蓝图系统-概念提出技术栈选型

请不要自我设限&#xff0c;真正好的人生态度&#xff0c;是现在就做&#xff0c;不等、不靠、不懒惰。 ——小野《改变力》 一、什么是蓝图&#xff1f; 蓝图(BluePrint) 是Epic Games 针对虚幻4引擎开发的可视化脚本语言。当你使用蓝图的时候&#xff0c;其实就是在编写代码…

【C++ 类和对象 基础篇】—— 抽象思维的巅峰舞者,演绎代码的深邃华尔兹

C学习笔记&#xff1a; C 进阶之路__Zwy的博客-CSDN博客 各位于晏&#xff0c;亦菲们&#xff0c;请点赞关注&#xff01; 我的个人主页&#xff1a; _Zwy-CSDN博客 目录 1、类 1.1、类的定义 1.2、访问限定符 1.2.1、public 1.2.2、private 1.2.3、protected 1.3、…

(高可用版本)Kubeadm+Containerd+keepalived部署高可用k8s(v1.28.2)集群

KubeadmContainerdkeepalived部署高可用k8s(v1.28.2)集群 一.环境准备&#xff0c;二.容器运行时Containerd安装请参照前文。KubeadmContainerd部署k8s(v1.28.2)集群&#xff08;非高可用版&#xff09;-CSDN博客 文章目录 KubeadmContainerdkeepalived部署高可用k8s(v1.28.2)集…

联合目标检测与图像分类提升数据不平衡场景下的准确率

联合目标检测与图像分类提升数据不平衡场景下的准确率 在一些数据不平衡的场景下&#xff0c;使用单一的目标检测模型很难达到99%的准确率。为了优化这一问题&#xff0c;适当将其拆解为目标检测模型和图像分类模型的组合&#xff0c;可以更有效地控制最终效果&#xff0c;尤其…

C++之红黑树模拟实现

目录 红黑树的概念 红黑树的性质 红黑树的查找效率 红黑树的实现 红黑树的定义 红黑树节点的插入 红黑树的平衡调整 判断红黑树是否平衡 红黑树整体代码 测试代码 上期我们学习了AVL树的模拟实现&#xff0c;在此基础上&#xff0c;我们本期将学习另一个数据结构-…