Unity InputActions Hold交互切换角色后移动失效问题求助
问题描述
设置了带有Hold交互的1D Axis绑定MoveX动作,场景内有4个角色,同一时间仅激活一个且支持角色切换。按住A或D键的状态下切换角色,新激活的角色会停止移动,必须松开并重新按下按键才能恢复移动。尝试在Update方法中检测按键状态未解决问题;删除OnDisable方法中的controls.Disable()代码可解决问题,但控制台会出现大量错误,需寻求正确解决方案。
原代码
using System.Collections; using System.Collections.Generic; using UnityEngine; public class CharacterController : MonoBehaviour { private StateManager stateManager; [SerializeField] private string name; private CharacterMovement movement; private CharacterJump jump; public Animator anim; public Rigidbody2D rb; private SpriteRenderer spriteRenderer; private InputActions controls; public float horizontal; public bool flipOnMove; public bool isGround; void Awake() { controls = new InputActions(); controls.Main.MoveX.performed += ctx => { horizontal = ctx.ReadValue<float>(); }; controls.Main.MoveX.canceled += ctx => { horizontal = 0; }; controls.Main.Jump.started += ctx => JumpStart(); controls.Main.Jump.canceled += ctx => JumpEnd(); stateManager = FindObjectOfType<StateManager>(); anim = GetComponent<Animator>(); rb = GetComponent<Rigidbody2D>(); spriteRenderer = GetComponent<SpriteRenderer>(); movement = GetComponent<CharacterMovement>(); jump = GetComponent<CharacterJump>(); } void Update() { if(flipOnMove) { switch(horizontal) { case -1: spriteRenderer.flipX = true; break; case 1: spriteRenderer.flipX = false; break; } } } void FixedUpdate() { Move(horizontal); } void OnEnable() { controls.Enable(); stateManager.currentGameObject = gameObject; stateManager.currentStatesName = name; if(stateManager.lastGameObjectTs != null) // 保存位置 { gameObject.transform.position = stateManager.lastGameObjectTs.position; if(name == "Human") // 如果是人类则生成在更高位置 gameObject.transform.position += new Vector3(0f,0.4f,0f); gameObject.transform.rotation = stateManager.lastGameObjectTs.rotation; } if(stateManager.lastGameObjectRb != null) // 保存物理状态 { Rigidbody2D rb = gameObject.GetComponent<Rigidbody2D>(); rb.velocity = stateManager.lastGameObjectRb; } } void OnDisable() { controls.Disable(); } public void Move(float direction) { if(movement != null) { movement.Move(direction); } } public void JumpStart() { if(jump != null) { jump.Jump(); } } public void JumpEnd() { if(jump != null) { jump.StopJumpAnim(); } } }
问题根源
旧角色禁用时调用controls.Disable(),会触发Input系统的canceled事件,将horizontal设为0。而新角色激活时,由于按键处于持续按住状态,Hold交互没有新的输入变化,Input系统不会自动触发performed事件,导致新角色的horizontal保持0,进而停止移动。
解决方案
方案1:使用全局单例InputActions(推荐)
让所有角色共享同一个InputActions实例,避免切换时频繁启用/禁用输入实例,从根源解决问题。
- 创建全局Input管理类:
public class InputManager : MonoBehaviour { public static InputActions Instance { get; private set; } void Awake() { if (Instance == null) { Instance = new InputActions(); Instance.Enable(); } else { Destroy(gameObject); } DontDestroyOnLoad(gameObject); } }
将该脚本挂载到场景中的全局对象(如空物体)上,确保场景切换时不会被销毁。
- 修改CharacterController的Awake方法,引用全局实例:
void Awake() { // 替换原有controls实例化代码 controls = InputManager.Instance; // 将lambda表达式改为独立方法,方便后续解绑 controls.Main.MoveX.performed += OnMoveXPerformed; controls.Main.MoveX.canceled += OnMoveXCanceled; controls.Main.Jump.started += OnJumpStarted; controls.Main.Jump.canceled += OnJumpCanceled; // 原有其他逻辑不变... } // 新增独立事件处理方法 private void OnMoveXPerformed(InputAction.CallbackContext ctx) { horizontal = ctx.ReadValue<float>(); } private void OnMoveXCanceled(InputAction.CallbackContext ctx) { horizontal = 0; } private void OnJumpStarted(InputAction.CallbackContext ctx) { JumpStart(); } private void OnJumpCanceled(InputAction.CallbackContext ctx) { JumpEnd(); }
- 修改OnDisable方法,仅解绑事件,不再禁用controls:
void OnDisable() { controls.Main.MoveX.performed -= OnMoveXPerformed; controls.Main.MoveX.canceled -= OnMoveXCanceled; controls.Main.Jump.started -= OnJumpStarted; controls.Main.Jump.canceled -= OnJumpCanceled; }
方案2:保留角色独立InputActions,手动同步输入状态
如果必须每个角色单独管理InputActions,可在新角色激活时手动读取当前输入值,同步到horizontal。
修改CharacterController的OnEnable方法:
void OnEnable() { controls.Enable(); // 手动读取当前MoveX的输入值,同步到horizontal horizontal = controls.Main.MoveX.ReadValue<float>(); // 原有其他逻辑不变... }
同时记得在OnDisable中解绑事件(同方案1的步骤3),避免内存泄漏和事件冲突。
总结
方案1通过全局单例统一管理输入,更简洁高效,能彻底避免切换角色时的输入中断问题;方案2适合需要角色独立输入配置的场景,通过手动同步输入状态解决问题。两种方案都需注意事件的解绑,防止内存泄漏。
内容的提问来源于stack exchange,提问作者Stas DIDUS

