You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity按钮动画问题:方向键垂直切换已实现,鼠标交互失效求助

Unity按钮交互冲突与动态生成解决方案

一、修复方向键动画与鼠标动画冲突问题

核心是用同一Animator Controller统一管理所有交互状态,避免两套动画逻辑互相覆盖。

1. 配置Animator状态机

创建一个Animator Controller,包含4个状态:

  • Normal:默认状态
  • Selected:方向键选中时的状态
  • Hover:鼠标悬停时的状态
  • Pressed:点击时的状态

状态切换条件设置:

  • Normal ↔ Selected:通过布尔参数IsSelected控制
  • Normal ↔ Hover:通过布尔参数IsHovered控制
  • 所有状态 → Pressed:通过触发参数OnPressed控制,Pressed自动回到对应前置状态(比如从Selected进入Pressed,退出后回到Selected)

2. 挂载状态同步脚本

给每个按钮添加如下脚本,统一处理方向键选中和鼠标交互的状态同步:

using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;

public class ButtonStateController : MonoBehaviour, IPointerEnterHandler, IPointerExitHandler, IPointerClickHandler
{
    private Animator _animator;
    private Button _button;

    private void Awake()
    {
        _animator = GetComponent<Animator>();
        _button = GetComponent<Button>();
    }

    // 由导航脚本调用,设置方向键选中状态
    public void SetSelected(bool isSelected)
    {
        _animator.SetBool("IsSelected", isSelected);
        // 选中时强制关闭hover状态,避免冲突
        if (isSelected)
        {
            _animator.SetBool("IsHovered", false);
        }
    }

    public void OnPointerEnter(PointerEventData eventData)
    {
        // 只有未被方向键选中时,才触发hover动画
        if (!_animator.GetBool("IsSelected"))
        {
            _animator.SetBool("IsHovered", true);
        }
    }

    public void OnPointerExit(PointerEventData eventData)
    {
        _animator.SetBool("IsHovered", false);
    }

    public void OnPointerClick(PointerEventData eventData)
    {
        _animator.SetTrigger("OnPressed");
        // 执行按钮点击逻辑
        _button.onClick?.Invoke();
    }
}

3. 适配原有方向键导航脚本

修改你原有的方向键切换脚本,确保选中按钮时调用SetSelected方法:

using UnityEngine;
using UnityEngine.UI;

public class MenuNavigation : MonoBehaviour
{
    public ButtonStateController[] buttons;
    private int _currentSelectedIndex = 0;

    private void Start()
    {
        // 默认选中第一个按钮
        buttons[_currentSelectedIndex].SetSelected(true);
    }

    private void Update()
    {
        if (Input.GetKeyDown(KeyCode.DownArrow))
        {
            Navigate(1);
        }
        else if (Input.GetKeyDown(KeyCode.UpArrow))
        {
            Navigate(-1);
        }
    }

    private void Navigate(int direction)
    {
        // 取消当前选中按钮的状态
        buttons[_currentSelectedIndex].SetSelected(false);

        // 计算新的选中索引
        _currentSelectedIndex += direction;
        _currentSelectedIndex = Mathf.Clamp(_currentSelectedIndex, 0, buttons.Length - 1);

        // 设置新选中按钮的状态
        buttons[_currentSelectedIndex].SetSelected(true);
        // 可选:让EventSystem同步选中状态,确保键盘输入正常
        EventSystem.current.SetSelectedGameObject(buttons[_currentSelectedIndex].gameObject);
    }
}

二、自动生成按钮的兼容性处理

自动生成按钮时,只需要确保以下几点,就能保留方向键切换功能:

1. 动态维护按钮列表

在导航脚本中用List<ButtonStateController>替代数组,生成按钮时自动添加到列表:

using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;

public class DynamicMenuNavigation : MonoBehaviour
{
    public Button buttonPrefab;
    public Transform buttonParent;
    private List<ButtonStateController> _buttons = new List<ButtonStateController>();
    private int _currentSelectedIndex = 0;

    public void GenerateButtons(string[] buttonTexts)
    {
        // 清空原有按钮
        foreach (Transform child in buttonParent)
        {
            Destroy(child.gameObject);
        }
        _buttons.Clear();

        // 生成新按钮
        foreach (string text in buttonTexts)
        {
            Button newButton = Instantiate(buttonPrefab, buttonParent);
            newButton.GetComponentInChildren<Text>().text = text;
            
            ButtonStateController stateController = newButton.GetComponent<ButtonStateController>();
            if (stateController == null)
            {
                stateController = newButton.AddComponent<ButtonStateController>();
            }
            _buttons.Add(stateController);
        }

        // 默认选中第一个按钮
        if (_buttons.Count > 0)
        {
            _buttons[_currentSelectedIndex].SetSelected(true);
        }
    }

    // 导航逻辑和之前的MenuNavigation一致,把数组换成_buttons列表即可
    private void Update()
    {
        if (_buttons.Count == 0) return;

        if (Input.GetKeyDown(KeyCode.DownArrow))
        {
            Navigate(1);
        }
        else if (Input.GetKeyDown(KeyCode.UpArrow))
        {
            Navigate(-1);
        }
    }

    private void Navigate(int direction)
    {
        _buttons[_currentSelectedIndex].SetSelected(false);

        _currentSelectedIndex += direction;
        _currentSelectedIndex = Mathf.Clamp(_currentSelectedIndex, 0, _buttons.Count - 1);

        _buttons[_currentSelectedIndex].SetSelected(true);
        EventSystem.current.SetSelectedGameObject(_buttons[_currentSelectedIndex].gameObject);
    }
}

2. 预制体配置

确保按钮预制体已经添加了Button、Animator(绑定好之前的状态机)、ButtonStateController组件,这样生成的每个按钮都自带完整的交互逻辑。

内容的提问来源于stack exchange,提问作者Han1t

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 14:26:43