【Unity InputSystem】实用指南:在PC端(鼠标与键盘)、手机端(触摸屏)、主机手柄上同步实现角色移动与跳跃功能

前引

随着Unity的不断发展,开发者对于项目的输入系统要求也日益提高。在进行多平台适配和跨平台移植时,常常需要改变输入系统,这给开发者带来了不少困扰。而Unity官方推出的InputSystem插件,则是为了解决这一问题而推出的全新输入方式。
相较于旧版的InputManager,InputSystem的操作虽然更为繁琐复杂,但在应对跨平台项目时,面对不同的输入方式,InputSystem的输入映射机制为开发者提供了巨大的便利。因此,学习InputSystem成为必要之举。


正文

PC端-键鼠🐀

创建PC对应的键鼠InputActions

在安装好InputSystem之后我们先在项目中创建InputActions,如下所示:


创建InputActions成功后双击打开InputActions编辑页面,创建一个ActionMaps命名为PC,之后再创建Map中对应的几个Actions分别命名为Move,CameraControl,Jump:


Move,CameraControl的Action Type选择Value, Control Type为返回值类型选择Vector2:


在MoveAction的输入绑定中我们需要进行以下操作 选择方向键混合绑定:


之后在每个方向检测上绑定对应的输入检测方式,每次变更完InputAction后记得点击Save Asset保存(或者勾选右侧的Auto Save自动保存):

创建好的InputActions后,我们可以在InputActions属性面板中找到Generate C# Class并勾选,随后点击Apply生成对应的脚本,之后我们就可以在我们自己写的PlayerController 类中调用该脚本了:

万事俱备,只欠代码!


PC脚本调用初始化

初始化:

using UnityEngine;
using UnityEngine.InputSystem;public class RPGController : MonoBehaviour
{public RPGInputActions rpgInputActions;private void Awake() {//实例化我们刚刚生成的rpgInputActions脚本rpgInputActions = new RPGInputActions();}void OnEnable(){//使用前需要将该rpgInputActions开启rpgInputActions.PC.Enable();}void OnDisable(){//使用完需要将该rpgInputActions关闭rpgInputActions.PC.Disable();}//Update生命周期函数private void Update() {}
}

PC获取Move Action输入:

    //将获取Move输入方法写在Update方法中逐帧调用private void Update() {getMoveInput();}//获取Move输入方法private void getMoveInput(){//将读取到的Move返回值赋值给moveVector2 Vector2 moveVector2 = rpgInputActions.PC.Move.ReadValue<Vector2>();//判断是否有按下对应的Move按键if (moveVector2  != Vector2.zero) {//将获取到的返回值打印出来Debug.Log(moveVector2);}}

PC获取Jump Action输入:

    //将获取Jump输入方法写在Update方法中逐帧调用private void Update() {getJumpInput();}//获取Jump输入方法private void getJumpInput(){//将读取到的Jump返回值赋值给isJump bool isJump = rpgInputActions.PC.Jump.IsPressed();//判断是否有按下对应的Jump按键if (isJump) {//将获取到的返回值打印出来Debug.Log(isJump);}}

PC获取CameraControl Action输入:

    //将获取CameraControl输入方法写在Update方法中逐帧调用private void Update() {getCameraControlInput();}//获取CameraControl输入方法private void getCameraControlInput(){//将读取到的CameraControl返回值赋值给cameraOffset Vector2 cameraOffset = rpgInputActions.PC.CameraControl.ReadValue<Vector2>();//判断是否有鼠标是否有产生偏移if (cameraOffset != Vector2.zero) {//将获取到的鼠标偏移值打印出来Debug.Log(cameraOffset );}}

将我们上面写好的脚本挂载到对应的游戏对象上,点击开始在项目运行时我们就可以通过刚刚绑定在Action上的按键触发我们写好的输入检测获取方法啦:


功能实现

既然我们可以获取到不同硬件设备的输入检测时,我们接下来就要将检测到的输入信号和我们实际的执行方法关联在一起,实现我们项目中的具体功能。我们具体功能的实现会以PC端鼠键输入检测为例。

键盘控制角色移动

根据上述PC端键鼠检测的操作,我们可以通过键盘的WASD或者小键盘的方向键获取到一个类型为Vector2的返回值。接下来我们将该方法进行一个小改动实现角色移动的功能:

public class RPGController : MonoBehaviour
{public RPGInputActions rpgInputActions;//声明一个移动速度属性初始值可以自行安排,我这里给个2private float moveSpeed = 2f;private void Update() {getMoveInput();}private void getMoveInput(){//将读取到的Move返回值赋值给moveVector2 Vector2 moveVector2 = rpgInputActions.PC.Move.ReadValue<Vector2>();//因为我们的playerMove会在Update生命周期函数中逐帧执行,所以在执行前需要判断是否有按下对应的按键if (moveVector2 != Vector2.zero) {//使用获取到的Vector2.x和Vector2.y返回值作为角色移动的参数playerMove(moveVector2.x,moveVector2.y);}}  //使角色真正移动的方法private void playerMove(float horizontal,float vertical){transform.Translate(new Vector3(horizontal,0,vertical)*Time.deltaTime*moveSpeed,Space.World);} 
}

来一点小小的优化让代码更加简洁:

public class RPGController : MonoBehaviour
{public RPGInputActions rpgInputActions;//声明一个移动速度属性初始值可以自行安排,我这里给个2private float moveSpeed = 2f;//表达式用于初始化moveAxis获取rpgInputActions.PC.Move的返回值赋值给moveAxisprivate Vector2 moveAxis => rpgInputActions.PC.Move.ReadValue<Vector2>();private void Update() {getMoveInput();}private void getMoveInput(){//因为我们的playerMove会在Update生命周期函数中逐帧执行,所以在执行前需要判断是否有按下对应的按键if (moveAxis!= Vector2.zero) {//使用获取到的Vector2.x和Vector2.y返回值作为角色移动的参数playerMove(moveVector2.x,moveVector2.y);}} //使角色真正移动的方法private void playerMove(float horizontal,float vertical){transform.Translate(new Vector3(horizontal,0,vertical)*Time.deltaTime*moveSpeed,Space.World);} 
}
键盘控制角色跳跃

在我们通过键盘的空格键获取到一个类型为bool的返回值。接下来我们就使用该返回值实现角色跳跃的功能:

public class RPGController : MonoBehaviour
{public RPGInputActions rpgInputActions;//获取对象刚体用于实现跳跃功能时给其施加一个向上的力public Rigidbody playerRigidbody;//表达式用于初始化isJump 获取rpgInputActions.PC.Jump是否有按下赋值给cameraOffset private bool isJump => rpgInputActions.PC.Jump.IsPressed();private void Update() {getJumpInput();}private void getJumpInput(){if (isJump) {//判断Jump输入设备被按下时给执行跳跃方法playerJump();}}//给刚体施加一个向上的力使角色模拟跳跃的效果public void playerJump(){playerRigidbody.AddForce(Vector3.up * 5);}
}
鼠标控制视角转动

在我们通过鼠标偏移获取到一个类型为Vector2的返回值。接下来我们就使用该返回值实现控制镜头的功能:

public class RPGController : MonoBehaviour
{public RPGInputActions rpgInputActions;//获取围绕旋转对象的Transform public Transform targetFollowTransform;//获取需要控制旋转的相机Transform public Transform cameraTransform;//表达式用于初始化moveAxis获取rpgInputActions.PC.CameraControl的返回值赋值给cameraOffset private Vector2 cameraOffset => rpgInputActions.PC.CameraControl.ReadValue<Vector2>();private void Update() {getCameraControlInput();}private void getCameraControlInput(){{//判断获取cameraControl的输入设备是否触发偏移值if(cameraOffset != Vector2.zero){//控制相机以targetFollowTransform对象为中心围绕世界坐标垂直轴进行水平旋转,旋转角度为cameraOffset水平方向返回的偏移值cameraTransform.RotateAround(targetFollowTransform.position, Vector3.up, cameraOffset.x);//控制相机以targetFollowTransform对象围绕相机自身水平轴进行垂直旋转,旋转角度为cameraOffset垂直方向返回的偏移值cameraTransform.RotateAround(targetFollowTransform.position, cameraTransform.right, -cameraOffset.y);}}} 
}

主机端-手柄🎮

创建主机对应的手柄InputActions

绑定MoveAction的触发操作

柄控制MoveAction的输入绑定中我们需要进行以下操作 选择普通的绑定即可:

绑定操作设备选择手柄Gamepad:

因为我们Move动作选择的返回值类型为Vector2所以手柄这里默认的触发行为默认的有三个D-Pad坐标方向键, Left Stick左摇杆, Right Stick右摇杆)根据我们喜好选择Gamepad上的触发操作(一般是选择左摇杆来控制移动):

绑定JumpAction的触发操作

在手柄控制JumpAction的输入绑定中前两步的选择都是一样
1.在JumpAction上点击+号选择普通的绑定即可
2.绑定操作设备选择手柄Gamepad
3.选择右边方向键的西边按键(因为不同厂商的右边方向键显示不同,所以用东南西北方向来表示):

绑定CameraControlAction的触发操作

在手柄控制CameraControlAction的输入绑定中前两步的选择都是一样
1.在CameraControlAction上点击+号选择普通的绑定即可
2.绑定操作设备选择手柄Gamepad
3.选择右摇杆(也可以根据自己喜欢选择其他触发操作):

功能实现

在手柄上的功能实现因为我们用的都是同一套的InputAction,所以我们只需要完成上述绑定手柄的对应Actions触发即可。 这里因为我们在PC端键鼠小节的时候已经把对应的脚本写好了,这里我们就不需要重新再写脚本了(这就是InputSystem区别于旧版InputManager最大的优势无需重新写代码)点击开始在项目运行时我们就可以通过刚刚绑定在Action上的按键触发我们写好的输入检测获取方法了。

移动端-触摸屏触摸屏📱

我们要获取移动端触摸屏输入检测的话可以通过两种方式 在获取之前我们需要做几个对应的准备工作:

PC鼠标模拟触控

使用PC上鼠标模拟触摸屏上点击实现模拟触控(仅能模拟单指触控)

我们需要点击Window=>Analysis =>Input Debugger:


打开Debugger视窗之后点击左上角的Options选择Simulate Touch Input:

添加新输入设备检测后可以看到输入设备栏中从原本只有鼠键两种检测新增多了一个模拟触摸屏,这就代表成功了。


功能实现

触摸屏控制角色移动

在我们的上述章节当中其实已经实现了PC端和主机端物体移动,接下来我们完成以下几步即可实现移动端触摸屏的物体移动功能。

将虚拟摇杆中获取到的Vector2返回值作为参数赋值到我们移动方法:

public class RPGController : MonoBehaviour
{public Joystick moveJoystick;private float moveSpeed = 2f;//Update生命周期函数private void Update() {getTouchScreenMove();}private void getTouchScreenMove(){//使用获取到的虚拟摇杆Joystick的Horizontal和Vertical返回值作为角色移动的参数playerMove(moveJoystick.Horizontal,moveJoystick.Vertical);}//使角色真正移动的方法private void playerMove(float horizontal,float vertical){transform.Translate(new Vector3(horizontal,0,vertical)*Time.deltaTime*moveSpeed,Space.World);}
}

将刚刚写的公共JoyStick变量进行绑定 3.完成上述两步,我们移动端的触摸屏虚拟摇杆触发物体移动功能就完成了:

触摸屏控制角色跳跃

1.找到我们的JumpButton组件

2.在Button的属性面板上面找到OnClick绑定

3.将挂载了我们已经写好了功能实现脚本的对象拖动挂载到OnClick上面

4.找到写好的跳跃方法进行绑定

触摸屏控制视角转动

在触摸屏上上述两种功能实现因为是用到了虚拟按钮没有使用到InputAction的触发绑定,所以需要完成以上两个小节才可以实现对应的具体功能。 但在视角旋转上面的触发操作我们是绑定了InputAction的,所以我们和前文手柄的功能实现一样无需做额外的操作即可实现对应的视角转动功能。

完成上述脚本编写和绑定CameraControl在触摸屏上触摸为触发操作以触发两帧之间的偏移值为返回值。那么我们在触摸屏上进行滑动触摸便可以进行视角的转动。

触摸屏触控操作优化

由于我们是在触摸屏上进行操作触发,上述流程虽然可以完成单独的功能实现,但在进行多点触控时可能会出现触发误判或冲突(例如,物体移动和视角转动功能之间存在冲突)。尽管移动功能是通过虚拟摇杆触发的,但同时触摸屏幕也可能触发视角转动功能。因此,我们需要对脚本进行升级,以区分触发的功能。

using UnityEngine.InputSystem;//引用InputSystem包
public class RPGController : MonoBehaviour
{public Joystick moveJoystick;private float moveSpeed = 2f;public GameInputActions gameInputActions;public Transform targetFollowTransform;public Transform cameraTransform;//Update生命周期函数private void Update() {getTouchScreenMove();}private void getTouchScreenMove(){//使用获取到的虚拟摇杆Joystick的Horizontal和Vertical返回值作为角色移动的参数playerMove(moveJoystick.Horizontal,moveJoystick.Vertical);}//使角色真正移动的方法private void playerMove(float horizontal,float vertical){transform.Translate(new Vector3(horizontal,0,vertical)*Time.deltaTime*moveSpeed,Space.World);}//获取CameraControl输入方法private void getCameraControlInput(){//将读取到的CameraControl返回值赋值给cameraOffset Vector2 cameraOffset = gameInputActions.PC.CameraControl.ReadValue<Vector2>();//判断是否有鼠标是否有产生偏移if (cameraOffset != Vector2.zero) {//     //将获取到的鼠标偏移值打印出来cameraTransform.RotateAround(targetFollowTransform.position, Vector3.up, cameraOffset.x);cameraTransform.RotateAround(targetFollowTransform.position, cameraTransform.right, -cameraOffset.y);}}//将物体移动方法和视角转动方法都放到该方法内进行统一判定private void touchControl(){//因为在触摸屏上有多点触发的可能性,因此我们需要获取到每个触发点的顺序for(int i=0;i<Input.touchCount;i++){//按照触发点的顺序来获取在屏幕上的对应位置进行功能判定Vector3 touchPos= Input.GetTouch(i).position;//如果触发点在屏幕左半屏即为移动功能触发,我们执行移动方法即可if(touchPos.x < Screen.width / 2){getTouchScreenMove();}//如果触发点在屏幕右半屏即为视角转动功能触发,我们执行视角转动方法即可if(touchPos.x > Screen.width / 2 && touchPos.y > Screen.height / 2){getCameraControlInput();}}}
}

在脚本进行了优化之后我们触摸屏就可以实现左右半屏单点触发对应的功能。

总结

以上就是我们InputSystem在不同设备上的一些实用技巧分享,另外还有更多详细实用的Unity教程系列在我的“YF的Unity世界”频道,欢迎大家一起来学习交流!

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

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

相关文章

Linux内存管理--系列文章壹

一、引子 作者、我在上班闲着没事的时候&#xff0c;看了一些关于Linux内存管理和程序装载、链接的文章&#xff0c;然后自己就总结出了一些东西。 本系列文章一方面将资料中的长篇大论总结到最少、以方便可以直接找到答案&#xff0c;一方面也是方便面试的时候可以吹牛逼。 L…

【Docker】golang使用DockerFile正确食用指南

【Docker】golang使用DockerFile正确食用指南 大家好 我是寸铁&#x1f44a; 总结了一篇golang使用DockerFile正确食用指南✨ 喜欢的小伙伴可以点点关注 &#x1f49d; 问题背景 今天寸铁想让编写好的go程序在docker上面跑&#xff0c;要想实现这样的效果&#xff0c;就需要用…

小程序 van-field label和输入框改成上下布局

在组件上面加个样式就行&#xff1a;custom-style"display:block;" <van-field label"备注说明" type"textarea" clearable title-width"100px" custom-style"display:block;" placeholder"请输入" /> …

大载重无人机基础技术,研发一款50KG负重六旋翼无人机技术及成本分析

六旋翼无人机是一种多旋翼无人机&#xff0c;具有六个旋翼&#xff0c;通常呈“X”形布局。它采用电动串列式结构&#xff0c;具有垂直起降、悬停、前飞、后飞、侧飞、俯仰、翻滚等多种飞行动作的能力。六旋翼无人机通常被用于航拍、农业植保、环境监测、地形测绘等领域。 六旋…

Day34-Linux网络管理4

Day34-Linux网络管理4 1. IP地址分类与子网划分基础1.1 什么是IP地址1.2 十进制与二进制的转换1.3 IP地址的分类1.4 私网地址和局域网地址 2. 通信类型3. 子网划分讲解3.1 为什么要划分子网&#xff1f;3.2 什么是子网划分&#xff1f;3.3 子网划分的作用&#xff1f;3.4 子网划…

云计算项目十一:构建完整的日志分析平台

检查k8s集群环境&#xff0c;master主机操作&#xff0c;确定是ready 启动harbor [rootharbor ~]# cd /usr/local/harbor [rootharbor harbor]# /usr/local/bin/docker-compose up -d 检查head插件是否启动&#xff0c;如果没有&#xff0c;需要启动 [rootes-0001 ~]# system…

如何使用Hexo搭建个人博客

文章目录 如何使用Hexo搭建个人博客环境搭建连接 Github创建 Github Pages 仓库本地安装 Hexo 博客程序安装 HexoHexo 初始化和本地预览 部署 Hexo 到 GitHub Pages开始使用发布文章网站设置更换主题常用命令 插件安装解决成功上传github但是web不更新不想上传文章处理方式链接…

Yolov8模型用torch_pruning剪枝

目录 &#x1f680;&#x1f680;&#x1f680;订阅专栏&#xff0c;更新及时查看不迷路&#x1f680;&#x1f680;&#x1f680; 原理 遍历所有分组 高级剪枝器 &#x1f680;&#x1f680;&#x1f680;订阅专栏&#xff0c;更新及时查看不迷路&#x1f680;&#x1f680…

JVM基本概念、命令、参数、GC日志总结

原文: 赵侠客 一、前言 NPE&#xff08;NullPointerException&#xff09;和OOM&#xff08;OutofMemoryError&#xff09;在JAVA程序员中扮演着重要的角色&#xff0c;它也是很多人始终摆脱不掉的梦魇&#xff0c;与NPE不同的是OOM一旦在生产环境中出现就意味着只靠代码已经无…

java集合题库详解

1. Arraylist与LinkedList区别 可以从它们的底层数据结构、效率、开销进行阐述哈 ArrayList是数组的数据结构&#xff0c;LinkedList是链表的数据结构。 随机访问的时候&#xff0c;ArrayList的效率比较高&#xff0c;因为LinkedList要移动指针&#xff0c;而ArrayList是基于索…

Java 客户端向服务端上传文件(TCP通信)

一、实验内容 编写一个客户端向服务端上传文件的程序&#xff0c;要求使用TCP通信的的知识&#xff0c;完成将本地机器输入的路径下的文件上传到D盘中名称为upload的文件夹中。并把客户端的IP地址加上count标识作为上传后文件的文件名&#xff0c;即IP&#xff08;count&#…

OpenSearch 与 Elasticsearch:哪个开源搜索引擎适合您?

当谈论到搜索引擎产品时&#xff0c;Elasticsearch 和 OpenSearch 是两个备受关注的选择。它们都以其出色的功能和灵活性而闻名&#xff0c;但在一些方面存在一些差异。在本文中&#xff0c;我们将从功能和延展性、工具与资源、价格和许可这三个角度对这两个产品进行论述。通过…

qt+opencv人脸人眼检测识别

项目运行涉及到opencv库&#xff0c;以及haarcascade_frontalface_default.xml和haarcascade_eye_tree_eyeglasses.xml。qt配置opencv可见先前文章&#xff0c;另外这两份OpenCV 中用于眼睛检测的级联分类器xml文件&#xff0c;是我在网上下载的。 把要使用到的文件都放到当前…

鸿蒙培训开发:就业市场的新热点~

金三银四在即&#xff0c;随着春节假期结束&#xff0c;各行各业纷纷复工复产&#xff0c;2024年的春季招聘市场也迎来了火爆的局面。最近发布的《2024年春招市场行情周报&#xff08;第一期&#xff09;》显示&#xff0c;尽管整体就业市场仍处于人才饱和状态&#xff0c;但华…

spring-cloud-openfeign 3.0.0(对应spring boot 2.4.x之前版本)之前版本feign整合ribbon请求流程

在之前写的文章配置基础上 https://blog.csdn.net/zlpzlpzyd/article/details/136060312 下图为自己整理的

Excel 快速填充/输入内容

目录 一. Ctrl D/R 向下/右填充二. 批量输入内容 一. Ctrl D/R 向下/右填充 ⏹如下图所示&#xff0c;通过快捷键向下和向右填充数据 &#x1f914;当选中第一个单元格之后&#xff0c;可以按住Shift后&#xff0c;再选中最后一个单元格&#xff0c;可以选中第一个单元格和最…

自动驾驶技术解析与关键步骤

目录 前言1 自动驾驶主要技术流程1.1 车辆周围环境感知1.2 车辆和行人检测分析1.3 运动轨迹规划 2 关键技术概述2.1 车辆探测与图片输入2.2 行人检测2.3 运动规划2.4 电子地图2.5 轨迹预测2.6 交通灯分析2.7 故障检测 结语 前言 自动驾驶汽车作为未来交通领域的重要发展方向&a…

【Python】-入门:安装配置和IDLE的使用

Python的安装和配置 一、下载Python安装包 首先&#xff0c;你需要从Python的官方网站&#xff08;https://www.python.org/downloads/&#xff09;下载适合你操作系统的Python安装包。请注意&#xff0c;Python 2.x版本即将停止维护&#xff0c;因此推荐下载Python 3.x版本。…

【LGR-176-Div.2】[yLCPC2024] 洛谷 3 月月赛 I(A~C and G<oeis>)

[yLCPC2024] A. dx 分计算 前缀和提前处理一下区间和&#xff0c;做到O&#xff08;1&#xff09;访问就可以过。 #include <bits/stdc.h> //#define int long long #define per(i,j,k) for(int (i)(j);(i)<(k);(i)) #define rep(i,j,k) for(int (i)(j);(i)>(k);…

Redis作为缓存的数据一致性问题

背景 使用Reids作为缓存的原因&#xff1a; 在高并发场景下&#xff0c;传统关系型数据库的并发能力相对比较薄弱&#xff08;QPS不能太大&#xff09;&#xff1b; 使用Redis做一个缓存。让用户请求先打到Redis上而不是直接打到数据库上。 但是如果出现数据更新操作&#xff…