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

如何实现Unity游戏内菜单按左右键循环滑动切换?

实现游戏菜单循环滑动切换的通用方案

需求说明

通过左右按键实现多个游戏内菜单(如Items、Map、Lore)的循环滑动切换:当前处于中间菜单时,右键切到下一个;切到最后一个菜单后,右键可回到第一个菜单,避免无响应状态,同时代码需通用化,杜绝硬编码。

当前代码的问题

现有逻辑仅通过固定偏移值移动画布,未处理循环切换的边界情况,且硬编码了偏移量(2560f),无法适配不同数量或布局的菜单。

通用实现方案

1. 重构数据结构与初始化

用有序列表替代字典维护菜单顺序(字典无序,无法保证切换逻辑可预测),同时缓存每个菜单的初始位置:

// 有序列表存储菜单画布,确保切换顺序可控
private List<GameObject> uiCanvasList = new List<GameObject>();
// 缓存每个画布的初始位置,避免多次滑动后偏移
private Dictionary<GameObject, Vector3> originalPositions = new Dictionary<GameObject, Vector3>();
// 记录当前激活的菜单索引
private int currentMenuIndex = 0;

private void Awake()
{
    gameOverScreen.SetActive(false);
    
    // 收集所有带"UICanvas"标签的画布并初始化
    foreach (GameObject canvas in FindObjectsOfType<GameObject>())
    {
        if (canvas.CompareTag("UICanvas"))
        {
            uiCanvasList.Add(canvas);
            originalPositions.Add(canvas, canvas.GetComponent<RectTransform>().anchoredPosition3D);
            // 默认只显示第一个菜单,其余隐藏
            canvas.SetActive(canvas == uiCanvasList[0]);
        }
    }
}

2. 实现循环切换核心逻辑

通过索引计算目标菜单,用取模运算处理循环边界,统一管理滑动动画:

// 左键切换到上一个菜单(循环)
public void LeftUIButton()
{
    currentMenuIndex = (currentMenuIndex - 1 + uiCanvasList.Count) % uiCanvasList.Count;
    SwitchToTargetMenu();
}

// 右键切换到下一个菜单(循环)
public void RightUIButton()
{
    currentMenuIndex = (currentMenuIndex + 1) % uiCanvasList.Count;
    SwitchToTargetMenu();
}

// 通用菜单切换方法
private void SwitchToTargetMenu()
{
    // 获取当前菜单和目标菜单
    GameObject prevMenu = uiCanvasList[(currentMenuIndex - 1 + uiCanvasList.Count) % uiCanvasList.Count];
    GameObject targetMenu = uiCanvasList[currentMenuIndex];
    
    float slideDistance = GetSlideDistance();
    
    // 执行滑动动画:当前菜单滑出,目标菜单滑入
    StartCoroutine(LerpCanvas(prevMenu, originalPositions[prevMenu] + new Vector3(slideDistance, 0, 0), uICanvasesSlidingTime));
    StartCoroutine(LerpCanvas(targetMenu, originalPositions[targetMenu], uICanvasesSlidingTime));
    
    // 切换激活状态
    prevMenu.SetActive(false);
    targetMenu.SetActive(true);
}

// 动态计算滑动距离,彻底移除硬编码
private float GetSlideDistance()
{
    // 方案1:基于屏幕宽度(适配全屏菜单)
    return Screen.width;
    // 方案2:基于菜单自身宽度(适配非全屏菜单)
    // return uiCanvasList[0].GetComponent<RectTransform>().rect.width;
    // 方案3:从配置文件读取(最灵活,支持动态调整)
    // return MenuConfig.SlideOffset;
}

3. 优化滑动动画逻辑

确保动画基于初始位置,避免多次滑动后位置偏差,还可添加缓动效果提升体验:

private IEnumerator LerpCanvas(GameObject canvas, Vector3 targetPosition, float duration)
{
    RectTransform rectTrans = canvas.GetComponent<RectTransform>();
    Vector3 startPos = rectTrans.anchoredPosition3D;
    float elapsedTime = 0f;

    while (elapsedTime < duration)
    {
        elapsedTime += Time.deltaTime;
        float t = elapsedTime / duration;
        // 添加平滑缓动,让动画更自然
        t = Mathf.SmoothStep(0f, 1f, t);
        rectTrans.anchoredPosition3D = Vector3.Lerp(startPos, targetPosition, t);
        yield return null;
    }

    // 强制设置最终位置,避免误差
    rectTrans.anchoredPosition3D = targetPosition;
}

关键优化点

  • 有序列表控顺序:替代无序字典,菜单切换顺序完全可控,可通过调整列表顺序自定义菜单序列
  • 循环索引计算:用(index ±1 + 总数) % 总数实现无缝循环,彻底解决边界无响应问题
  • 动态偏移计算:通过屏幕宽度、菜单宽度或配置文件获取滑动距离,移除硬编码数值
  • 初始位置缓存:确保每次滑动都基于原始位置,防止多次操作后菜单位置偏移

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 06:05:55