【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.hqwc.cn/news/525958.html

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

相关文章

ChatGPT 控制机器人的基本框架

过去的一年&#xff0c;OpenAI的chatGPT将自然语言的大型语言模型&#xff08;LLM&#xff09;推向了公众的视野&#xff0c;人工智能AI如一夜春风吹遍了巴黎&#xff0c;全世界都为AI而疯狂。 OpenAI ChatGPT是一个使用人类反馈进行微调的预训练生成文本模型。不像以前的模型主…

2024 年中国高校大数据挑战赛赛题 D:行业职业技术培训能力评价完整思路以及源代码分享

中国是制造业大国&#xff0c;产业门类齐全&#xff0c;每年需要培养大量的技能娴 熟的技术工人进入工厂。某行业在全国有多所不同类型&#xff08;如国家级、 省级等&#xff09;的职业技术培训学校&#xff0c;进行 5 种技能培训。学员入校时需要 进行统一的技能考核&#xf…

Pb量级超大容量光存储

近日&#xff0c;中国科学院上海光学精密机械研究所&#xff08;以下简称“上海光机所”&#xff09;与上海理工大学等科研单位合作&#xff0c;在超大容量三维超分辨光存储研究中取得突破性进展。研究团队利用国际首创的双光束调控聚集诱导发光超分辨光存储技术&#xff0c;实…

unity学习(53)——选择角色界面--分配服务器返回的信息

好久没写客户端了&#xff0c;一上手还不太适应 1.经过测试&#xff0c;成功登陆后&#xff0c;客户端请求list_request&#xff0c;成功返回&#xff0c;如下图&#xff1a; 可见此时model第三个位置的参数是1.也成功返回了所有已注册角色的信息。 2.之前已知创建的角色信息…

CentOS Linux - Primavera P6EPPM安装部署

引言 根据计划&#xff0c;近期我制作了多套基于ORACLE Primavera P6 最新发布的23.12版本预构建了虚拟机环境&#xff0c;里面包含了全套P6 最新版应用服务 此虚拟机仅用于演示、培训和测试目的。如您在生产环境中使用此虚拟机&#xff0c;请先与Oracle Primavera销售代表取得…

【Linux】第四十一站:线程控制

一、Linux线程VS进程 1.进程和线程 进程是资源分配的基本单位线程是调度的基本单位线程共享进程数据&#xff0c;但也拥有自己的一部分数据:线程ID一组寄存器&#xff08;上下文&#xff09;栈errno信号屏蔽字调度优先级 2.进程的多个线程共享 同一地址空间,因此Text Segment、…

vue 在什么情况下在数据发生改变的时候不会触发视图更新

在 Vue 中&#xff0c;通常数据发生变化时&#xff0c;视图会自动更新。但是&#xff0c;有几种情况可能导致数据变化不会触发视图更新&#xff1a; 1.对象属性的添加或删除&#xff1a; Vue 无法检测到对象属性的添加或删除。因为 Vue 在初始化实例时对属性执行了 getter/se…

怎么查看电脑是不是固态硬盘?简单几个步骤判断

随着科技的发展&#xff0c;固态硬盘&#xff08;Solid State Drive&#xff0c;简称SSD&#xff09;已成为现代电脑的标配。相较于传统的机械硬盘&#xff0c;固态硬盘在读写速度、稳定性和耐用性等方面都有显著优势。但是&#xff0c;对于不熟悉电脑硬件的用户来说&#xff0…

三步实现支付宝支付【go语言 支付宝沙箱】

支付宝沙箱支付使用背景&#xff1a; 支付宝沙箱支付是支付宝提供的一个测试环境&#xff0c;用于开发者在不影响真实交易的情况下进行支付接口的开发和调试。在沙箱环境中&#xff0c;开发者可以模拟真实的支付流程&#xff0c;包括支付、退款、查询等操作&#xff0c;以便更…

Spring Boot 自动装配的原理!!!

SpringBootApplication SpringBootConfiguration&#xff1a;标识启动类是一个IOC容器的配置类 EnableAutoConfiguration&#xff1a; AutoConfigurationPackage&#xff1a;扫描启动类所在包及子包中所有的组件&#xff0c;生…

【深度学习笔记】6_9 深度循环神经网络deep-rnn

注&#xff1a;本文为《动手学深度学习》开源内容&#xff0c;部分标注了个人理解&#xff0c;仅为个人学习记录&#xff0c;无抄袭搬运意图 6.9 深度循环神经网络 本章到目前为止介绍的循环神经网络只有一个单向的隐藏层&#xff0c;在深度学习应用里&#xff0c;我们通常会用…

【Java网络编程】TCP核心特性(下)

1. 拥塞控制 拥塞控制&#xff1a;是基于滑动窗口机制下的一大特性&#xff0c;与流量控制类似都是用来限制发送方的传送速率的 区别就在于&#xff1a;"流量控制"是从接收方的角度出发&#xff0c;根据接收方剩余接收缓冲区大小来动态调整发送窗口的&#xff1b;而…