Unity如何实现光标与方向导航(WASD/手柄)的无缝切换?
Unity中,通过方向导航(WASD、方向键、手柄Dpad)选中的Selectable(如按钮),与鼠标悬停触发的选中状态是相互独立的。这会引发一个常见问题:当鼠标悬停在某个按钮上时按下方向键,屏幕上会同时出现两个“选中”状态的按钮——一个是鼠标触发的高亮,另一个是方向导航触发的选中,但提交操作只会响应方向导航选中的那个按钮。这种视觉混淆极易误导用户,而且鼠标触发的高亮按钮不会设置事件系统的currentSelectedGameObject,无法直接清除残留的高亮状态。
我没有找到现成的可行解决方案,因此分享自己的实现方法,帮助遇到同类问题的开发者:
期望的无缝切换行为
用户可在鼠标、键盘、手柄控制模式间自由切换,实现光标与方向导航(WASD/Dpad)的无缝衔接:
- 从光标切换到方向导航时,若当前无选中项,则选中屏幕预设的首个按钮*;
- 若已有选中项,从当前选中项继续导航(例如选中第3个按钮时按上方向键,会选中第2个按钮);
- 从方向导航切换到光标时,清除所有选中状态(因为此时鼠标大概率未悬停在任何元素上)。
*注:我有自定义脚本可设置每个屏幕的“首个选中按钮”。
解决方案
核心思路:为每个Selectable绑定鼠标事件,在PointerEnter时触发OnSelect事件,PointerExit时触发OnDeselect事件;切换到方向输入模式时,限制鼠标移动区域,绕过Unity无法通过代码直接修改系统鼠标位置的限制。
我使用Rewired插件实现输入模式切换,切换到方向输入时触发事件,将鼠标的“移动区域”宽高设为0:

仅限制鼠标移动区域无法清除鼠标悬停的高亮(因为这种操作相当于“瞬移”光标,不会触发按钮的PointerExit事件),因此需要给所有Selectable元素挂载以下核心逻辑脚本(注:我结合了项目中的UI管理器,你可能需要根据自身需求调整):
public class ForceSelectedState: MonoBehaviour, IPointerEnterHandler, IPointerExitHandler { public UnityEngine.UI.Button button; public void OnPointerEnter(PointerEventData eventData) { button.OnSelect(eventData); } public void OnPointerExit(PointerEventData eventData) { button.OnDeselect(eventData); } }
请确保该脚本挂载在所有需要处理的Selectable元素上。
效果对比
- 默认行为:

- 优化后行为:

如果不使用Rewired,你可以寻找其他方式限制鼠标移动区域(系统鼠标位置为只读属性,无法通过代码直接修改)。
内容的提问来源于stack exchange,提问作者The Embraced One

