Unity新输入系统射击动作多次触发问题求助
Unity新输入系统Shoot动作多次触发的解决办法
问题说明
我是Unity游戏开发新手,正在学习新输入系统,已经实现3D第一人称移动功能,但添加的Shoot动作会多次触发(有时多达10次),控制台能观测到这个现象。尝试将输入事件从performed改为started后仍未解决问题。
相关代码
PlayerInputHandler.cs
[Header("Input Action Asset")] [SerializeField] private InputActionAsset playerControls; [Header("Action Map Name References")] [SerializeField] private string actionMapName = "Gameplay"; private InputAction moveAction; private InputAction lookAction; private InputAction jumpAction; private InputAction sprintAction; private InputAction interactAction; private InputAction shootAction; public Vector2 MoveInput { get; private set; } public Vector2 LookInput { get; private set; } public bool JumpTriggered { get; private set; } public float SprintValue { get; private set; } public bool InteractTriggered { get; private set; } public bool ShootTriggered { get; private set; } public static PlayerInputHandler instance { get; private set; } private void Awake() { if(instance == null) { instance = this; DontDestroyOnLoad(gameObject); } else { Destroy(gameObject); } moveAction = playerControls.FindActionMap(actionMapName).FindAction("Move"); lookAction = playerControls.FindActionMap(actionMapName).FindAction("Look"); jumpAction = playerControls.FindActionMap(actionMapName).FindAction("Jump"); sprintAction = playerControls.FindActionMap(actionMapName).FindAction("Sprint"); interactAction = playerControls.FindActionMap(actionMapName).FindAction("Interact"); shootAction = playerControls.FindActionMap(actionMapName).FindAction("Shoot"); RegisterInputActions(); } private void RegisterInputActions() { moveAction.performed += context => MoveInput = context.ReadValue<Vector2>(); moveAction.canceled += context => MoveInput = Vector2.zero; lookAction.performed += context => LookInput = context.ReadValue<Vector2>(); lookAction.canceled += context => LookInput = Vector2.zero; jumpAction.performed += context => JumpTriggered = true; jumpAction.canceled += context => JumpTriggered = false; sprintAction.performed += context => SprintValue = context.ReadValue<float>(); sprintAction.canceled += context => SprintValue = 0f; interactAction.performed += context => InteractTriggered = true; interactAction.canceled += context => InteractTriggered = false; shootAction.performed += context => ShootTriggered = true; shootAction.canceled += context => ShootTriggered = false; } private void OnEnable() { moveAction.Enable(); lookAction.Enable(); jumpAction.Enable(); sprintAction.Enable(); interactAction.Enable(); shootAction.Enable(); } private void OnDisable() { moveAction.Disable(); lookAction.Disable(); jumpAction.Disable(); sprintAction.Disable(); interactAction.Disable(); shootAction.Disable(); }
枪械逻辑代码
[SerializeField] private float damage = 10f; [SerializeField] private float range = 100f; private PlayerInputHandler inputHandler; private Camera cam; void Start() { cam = Camera.main; inputHandler = PlayerInputHandler.instance; } void Update() { if(inputHandler.ShootTriggered) { shoot(); } } private void shoot() { RaycastHit hit; if(Physics.Raycast(cam.transform.position, cam.transform.forward, out hit, range)) { Debug.Log(hit.transform.name); Debug.DrawRay(cam.transform.position, cam.transform.forward * range); Target target = hit.transform.GetComponent<Target>(); if(target != null) { target.TakeDamage(damage); } } }
问题根源
当前代码中,ShootTriggered在shootAction.performed时被设为true,直到canceled事件触发才会变回false。而枪械逻辑在Update中每帧检查这个布尔值,意味着只要按住射击按键(或鼠标键),每帧都会调用一次shoot()方法;即使是单次点击,也可能因为输入事件与帧循环的同步问题,导致连续几帧ShootTriggered保持true,从而多次触发射击。
解决方案
这里提供两种可靠的解决方式:
方式一:单次触发后立即重置标记
修改PlayerInputHandler,添加重置ShootTriggered的方法,然后在枪械逻辑调用射击后立刻重置标记:
- 在
PlayerInputHandler.cs中新增方法:
public void ResetShootTrigger() { ShootTriggered = false; }
- 修改枪械代码的
Update方法:
void Update() { if(inputHandler.ShootTriggered) { shoot(); inputHandler.ResetShootTrigger(); // 触发后立刻重置标记 } }
方式二:使用事件驱动(更推荐)
放弃在Update中轮询标记的方式,改用输入事件直接绑定射击逻辑,这样每次输入只会触发一次射击:
- 在
PlayerInputHandler.cs中添加射击事件,并修改shootAction的绑定:
// 新增射击事件 public event System.Action OnShoot; private void RegisterInputActions() { // ... 保留其他输入绑定代码 ... // 替换原有的shootAction绑定逻辑 shootAction.performed += context => OnShoot?.Invoke(); // 不需要再设置ShootTriggered,也不需要canceled绑定 }
- 修改枪械代码,订阅事件并移除
Update中的轮询:
void Start() { cam = Camera.main; inputHandler = PlayerInputHandler.instance; // 订阅射击事件 inputHandler.OnShoot += shoot; } // 记得取消订阅,避免内存泄漏 void OnDisable() { if(inputHandler != null) { inputHandler.OnShoot -= shoot; } } // 移除原有的Update方法中的检查逻辑 // void Update() // { // if(inputHandler.ShootTriggered) // { // shoot(); // } // }
额外注意事项
- 如果你需要支持按住按键连续射击,可以在事件驱动的基础上,结合
started和canceled事件配合协程实现,比如started时启动协程每隔固定时间调用射击,canceled时停止协程。 - 确保输入系统中Shoot动作的交互类型设置正确:如果是鼠标左键,建议设为
Button类型,触发方式为Press,避免重复触发。
内容的提问来源于stack exchange,提问作者Fadzii
相关产品推荐
相关产品推荐

