提问人:teeeeee 提问时间:6/2/2023 最后编辑:derHugoteeeeee 更新时间:6/6/2023 访问量:225
如何在不手动检查每一帧输入的情况下触发不频繁的交互事件?
How to trigger infrequent interaction events without manually checking for input every frame?
问:
我是Unity的新手,有一个非常基本的问题。
到目前为止,我一直在检查是否每一帧都按下一个键(仅在主“Update()”方法中),以处理玩家的移动。这很好,因为我希望无论如何几乎每一帧都会发生运动。
我的问题是关于如何处理不经常发生的事件——比如每 10 分钟发生一次。例如,暂停游戏。如果按下了暂停键,我不想检查每一帧,如下所示:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameController : MonoBehaviour
{
void Update()
{
if ( Input.GetKey(KeyCode.UpArrow) ) {
// Move (this happens pretty much every frame)
}
if ( Input.GetKey(KeyCode.Space) ) {
// Pause game (this hardly ever happens)
}
// Check for many more possible interactions ...
}
}
可能有许多这样的不频繁事件(火灾、跳跃等)。我知道它不应该真的减慢游戏的速度,有很多“if”语句,但如果这可以通过回调风格的操作来完成,那就更好了。
我知道 c#/Unity 中的代表和侦听器,但这些实际上是一种处理事件被触发/发布后发生的事情的方法,所以这并不是我的问题。此外,我更想知道如何在不检查每一帧的情况下触发这样的事件。在Unity中可能吗?
答:
经过一些广泛的研究,我得出的结论是 InputSystem
包是您最好的选择。
它在后端的处理方式非常特定于平台,这就是 Unity 自己编写包的原因。
要实现与 C# 中的查询类似的功能,您必须使用命名空间和一个类,该类再次特定于平台,并且由于不兼容问题,该程序集将无法与 Unity 一起使用。不过,您可以在引擎之外使用 C# 执行此操作。如果您非常好奇,请查看此链接。System.Windows.Forms
Control.KeyPress Event
在所有其他情况下,请放心地依赖,因为这正是您所要求的。InputSystem
正如 Phillip Zoghbi 的评论和回答中所建议的那样,输入系统包确实提供了我正在寻找的行为。如果它对任何人有帮助,下面给出了这种方法的最低工作示例。
首先,确保从“包管理器”菜单安装了“输入系统”。
然后,右键单击项目 Assets 文件夹 --> “Create” --> “Input Action”。打开它并创建一个操作映射,以及移动键和暂停按钮的操作和绑定:
接下来,为此输入操作资产使用“生成 C# 类”文件,它会自动创建一个 C# 文件(对我来说称为 )。MyInputActions.cs
现在我还有两个脚本。第一个是处理所有游戏输入:MyGameInputs.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MyGameInputs : MonoBehaviour
{
public event EventHandler OnPauseGameAction;
private MyInputActions myInputActions;
private void Awake(){
myInputActions = new MyInputActions();
myInputActions.MyActionMap.Enable();
myInputActions.MyActionMap.PauseGame.performed += PauseGame_performed;
}
private void PauseGame_performed( UnityEngine.InputSystem.InputAction.CallbackContext obj){
OnPauseGameAction?.Invoke(this, EventArgs.Empty);
}
public Vector3 getMoveDir(){
Vector3 moveDirection = myInputActions.MyActionMap.Move.ReadValue<Vector3>();
return moveDirection;
}
}
第二个是播放器的控制器脚本,它对输入做出反应:MyPlayerController.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MyPlayerController : MonoBehaviour
{
[SerializeField] private MyGameInputs myGameInputs;
private Vector3 moveDirection;
private void Start(){
myGameInputs.OnPauseGameAction += myGameInputs_OnPauseGameAction;
}
private void myGameInputs_OnPauseGameAction(object sender, System.EventArgs e){
Debug.Log("Pause Key Pressed");
// Perform actions when pause key pressed
}
private void Update(){
moveDirection = myGameInputs.getMoveDir();
Debug.Log(moveDirection);
// Act according to move input keys
}
}
move 函数的行为方式与原始帖子中大致相同(从某种意义上说,它仍然在检查每一帧的移动输入),但现在使用新输入系统中的方法。ReadValue()
但是,现在“暂停游戏”函数是使用回调而不是轮询完成的。这是通过在脚本中使用操作的“已执行”阶段来完成的,其中函数会触发一个名为 的事件。MyGameInputs.cs
PauseGame_performed()
OnPauseGameAction
最后,脚本正在侦听事件,并将在触发时运行该函数。MyPlayerController.cs
OnPauseGameAction
myGameInputs_OnPauseGameAction
这样,回调用于执行触发(而不是轮询),并且输入脚本和播放器脚本都是完全解耦的(通过事件通知完成通信)。
简短的回答,不,你不能
检查每一帧的输入是必要的,并且仍然会使用基于事件的输入系统在引擎盖下完成,没有什么比这更优化的了。
使用“新输入系统”,您已经可以访问这些事件,使用“旧输入管理器”,您必须创建自己的事件
如果需要,您可以轻松地自己创建活动。
using UnityEngine;
public class InputEvents : MonoBehaviour
{
public event Action OnPausePressed;
[SerializedField] private KeyCode _pauseKey;
[SerializedField] private string _pauseButton;
private void Update()
{
if (Input.GetKeyDown(_pauseKey) || Input.GetButtonDown(_pauseButton)) {
OnPausePressed?.Invoke();
}
}
}
public class MyOtherClass {
public void MyMethodToRegister() {
InputEvents.OnPausePressed -= PausePressed;
InputEvents.OnPausePressed += PausePressed;
}
public void MyMethodToUnregister() {
InputEvents.OnPausePressed -= PausePressed;
}
private void PausePressed() {
Debug.Log("Pause the game here");
}
}
评论
Input