Unity移动端商店按钮控制循环滚动系统实现方案咨询
移动端游戏商店滚动系统的纯代码实现方案
核心思路
放弃Scroll Rect,直接通过控制容器位置+UI元素管理实现滚动逻辑:用带遮罩的水平容器装载所有物品,点击左右按钮时通过代码调整容器的水平偏移量,同时保留一个重叠物品明确滚动方向;物品移除后直接更新列表并调整容器位置,逻辑更直观,避免Scroll Rect的偏移计算问题。
具体实现步骤
1. UI结构搭建
- 带
HorizontalLayoutGroup组件的容器:自动排列物品,支持设置间距 - 左右滚动控制按钮:用于触发前后滚动逻辑
- 外层
Mask组件:仅显示指定区域(比如单次显示3个物品,第三个与下一个物品保留部分重叠) - 物品预制体:每个物品绑定购买按钮,点击触发移除逻辑
2. 核心变量与初始化
定义关键变量记录状态,初始化时统计物品列表、计算物品总宽度:
using UnityEngine; using UnityEngine.UI; using System.Collections.Generic; public class ShopScrollController : MonoBehaviour { public HorizontalLayoutGroup itemContainer; public Button leftBtn; public Button rightBtn; public float overlapWidth = 50f; // 重叠元素宽度,用于明确滚动方向 public int visibleItemCount = 3; // 单次可见物品数量 private List<GameObject> itemList = new List<GameObject>(); private float itemTotalWidth; // 单个物品+布局间距的总宽度 private int currentStartIndex = 0; // 当前显示的起始物品索引 void Start() { // 初始化物品列表 foreach (Transform child in itemContainer.transform) { itemList.Add(child.gameObject); } // 计算单个物品的总宽度(物品宽度+布局间距) if (itemList.Count > 0) { RectTransform itemRect = itemList[0].GetComponent<RectTransform>(); itemTotalWidth = itemRect.rect.width + itemContainer.spacing; } UpdateButtonStates(); } }
3. 滚动逻辑实现
点击按钮时计算目标偏移量,支持平滑/即时切换:
// 向右滚动按钮点击事件 public void OnRightBtnClick() { int maxStartIndex = itemList.Count - visibleItemCount; if (currentStartIndex >= maxStartIndex) return; currentStartIndex++; // 计算容器目标位置(向左偏移,实现物品右滚效果) float targetPosX = -currentStartIndex * (itemTotalWidth - overlapWidth); // 平滑切换:用LeanTween/DOTween,直接赋值则为即时切换 LeanTween.moveX(itemContainer.GetComponent<RectTransform>(), targetPosX, 0.3f); UpdateButtonStates(); } // 向左滚动按钮点击事件 public void OnLeftBtnClick() { if (currentStartIndex <= 0) return; currentStartIndex--; float targetPosX = -currentStartIndex * (itemTotalWidth - overlapWidth); LeanTween.moveX(itemContainer.GetComponent<RectTransform>(), targetPosX, 0.3f); UpdateButtonStates(); }
4. 物品移除逻辑
购买后移除物品,自动调整显示状态避免空白:
// 移除指定物品(绑定到物品的购买按钮) public void RemoveItem(GameObject item) { int removeIndex = itemList.IndexOf(item); if (removeIndex == -1) return; itemList.Remove(item); Destroy(item); // 如果移除的是当前显示范围内的物品,调整起始索引 if (removeIndex < currentStartIndex + visibleItemCount) { currentStartIndex = Mathf.Max(currentStartIndex - 1, 0); // 刷新容器位置 float targetPosX = -currentStartIndex * (itemTotalWidth - overlapWidth); itemContainer.GetComponent<RectTransform>().anchoredPosition = new Vector2(targetPosX, 0); } UpdateButtonStates(); }
5. 边界与数量限制
- 按钮状态更新:滚动到首尾时自动隐藏对应按钮
- 数量限制:添加物品时判断总数,最多8个
// 更新左右按钮显示状态 private void UpdateButtonStates() { leftBtn.gameObject.SetActive(currentStartIndex > 0); bool isAtEnd = currentStartIndex + visibleItemCount >= itemList.Count; rightBtn.gameObject.SetActive(!isAtEnd); } // 添加物品(限制最多8个) public void AddItem(GameObject itemPrefab) { if (itemList.Count >= 8) return; GameObject newItem = Instantiate(itemPrefab, itemContainer.transform); itemList.Add(newItem); // 重新计算物品宽度(物品尺寸一致可跳过) RectTransform itemRect = newItem.GetComponent<RectTransform>(); itemTotalWidth = itemRect.rect.width + itemContainer.spacing; UpdateButtonStates(); }
方案优势
- 完全无需Scroll Rect,避免
horizontalNormalizedPosition因物品数量变化导致的偏移异常 - 物品移除逻辑更直接,自动调整显示状态,无需额外跟踪偏移值
- 滚动步长由物品宽度和重叠宽度决定,适配不同尺寸的物品
- 代码逻辑清晰,移动端性能开销低
内容的提问来源于stack exchange,提问作者Not Available
相关产品推荐
相关产品推荐

