Unity UI交错砖格(Running bond)布局:动态计算行列求助
Unity 动态交错砖格(Running Bond)布局实现方案
核心思路
放弃嵌套Horizontal/Vertical Layout Group的静态分组方案,改用自定义布局逻辑直接计算每个砖块的位置。这种方式可以灵活适配父容器尺寸变化,支持无限数量的砖块(结合对象池),完美解决多行列的交错对齐问题。
实现步骤
1. 基础配置定义
先明确布局的关键参数,方便后续调整:
- 砖块尺寸:固定2:1比例,定义
brickWidth,高度自动计算为brickWidth / 2 - 间距:分别定义
horizontalSpacing(砖块水平间距)和verticalSpacing(行之间的额外垂直间距) - 容器尺寸:获取ScrollRect Viewport的有效宽度,作为布局的最大可用宽度
2. 动态计算行列数
每行砖块数计算
- 奇数行(第1、3、5...行):直接用可用宽度计算最大容纳数,公式:
oddRowCount = Mathf.FloorToInt((availableWidth + horizontalSpacing) / (brickWidth + horizontalSpacing)) - 偶数行(第2、4、6...行):需要偏移半个砖宽+半个间距,所以可用宽度先减去
(brickWidth + horizontalSpacing)/2,再计算:evenRowCount = Mathf.FloorToInt((availableWidth + horizontalSpacing - (brickWidth + horizontalSpacing)/2) / (brickWidth + horizontalSpacing))
总行数计算
交替使用奇偶行的砖块数,从总砖块数中逐行扣除,直到剩余砖块数为0,记录每行的实际砖块数量。
3. 砖块位置计算
- 垂直方向:从Content顶部开始布局,奇数行的Y坐标为
currentY,偶数行需要偏移半个砖高(brickHeight/2),行之间的间隔为brickHeight/2 + verticalSpacing - 水平方向:奇数行从左 padding 开始,每个砖块的X坐标为
startX + i*(brickWidth + horizontalSpacing);偶数行的起始X坐标偏移(brickWidth + horizontalSpacing)/2
4. 适配容器尺寸变化
重写OnRectTransformDimensionsChange()方法,在父容器(Viewport)尺寸变化时自动触发布局刷新。
5. 无限数量支持(对象池优化)
如果需要支持大量砖块,不要一次性实例化所有对象:
- 维护一个砖块对象池,回收不可见的砖块
- 监听ScrollRect的滚动事件,计算当前可见的行范围,只实例化/复用可见区域内的砖块
6. 悬停缩放适配
- 将砖块的RectTransform锚点设为
(0,0),Pivot设为(0.5,0.5),确保缩放时以砖块中心为原点,不会偏移位置 - 阴影效果使用Unity自带的
Shadow组件,调整阴影的Effect Distance和Color,避免影响布局层级
代码示例
using UnityEngine; using System.Collections.Generic; public class RunningBondLayout : MonoBehaviour { [Header("布局配置")] public RectTransform brickPrefab; public float brickWidth = 200f; public float horizontalSpacing = 20f; public float verticalSpacing = 10f; public RectTransform viewport; [Header("数据配置")] public int totalBrickCount = 30; private List<RectTransform> activeBricks = new List<RectTransform>(); private void Start() { RefreshLayout(); } // 容器尺寸变化时自动刷新 private void OnRectTransformDimensionsChange() { RefreshLayout(); } // 手动触发布局刷新 public void RefreshLayout() { // 回收现有砖块(对象池模式下改为放回池,此处简化为销毁) foreach (var brick in activeBricks) { Destroy(brick.gameObject); } activeBricks.Clear(); float availableWidth = viewport.rect.width; float brickHeight = brickWidth / 2f; // 计算奇偶行的最大砖块数 int oddRowMax = Mathf.FloorToInt((availableWidth + horizontalSpacing) / (brickWidth + horizontalSpacing)); int evenRowMax = Mathf.FloorToInt((availableWidth + horizontalSpacing - (brickWidth + horizontalSpacing)/2f) / (brickWidth + horizontalSpacing)); // 生成每行的实际砖块数列表 List<int> rowCounts = new List<int>(); int remaining = totalBrickCount; bool isOddRow = true; while (remaining > 0) { int take = isOddRow ? oddRowMax : evenRowMax; take = Mathf.Min(take, remaining); rowCounts.Add(take); remaining -= take; isOddRow = !isOddRow; } // 设置Content高度 float contentHeight = verticalSpacing * (rowCounts.Count + 1) + brickHeight * rowCounts.Count + (rowCounts.Count - 1) * (brickHeight / 2f); GetComponent<RectTransform>().sizeDelta = new Vector2(0, contentHeight); // 逐个放置砖块 float currentY = contentHeight - verticalSpacing - brickHeight; for (int rowIdx = 0; rowIdx < rowCounts.Count; rowIdx++) { bool currentIsOdd = rowIdx % 2 == 0; int brickCount = rowCounts[rowIdx]; float startX = verticalSpacing; if (!currentIsOdd) { startX += (brickWidth + horizontalSpacing) / 2f; } for (int brickIdx = 0; brickIdx < brickCount; brickIdx++) { RectTransform brick = Instantiate(brickPrefab, transform); brick.sizeDelta = new Vector2(brickWidth, brickHeight); brick.anchorMin = Vector2.zero; brick.anchorMax = Vector2.zero; brick.pivot = new Vector2(0.5f, 0.5f); float xPos = startX + brickIdx * (brickWidth + horizontalSpacing); float yPos = currentY - (currentIsOdd ? 0 : brickHeight / 2f); brick.anchoredPosition = new Vector2(xPos, yPos); activeBricks.Add(brick); } // 更新下一行Y坐标 currentY -= brickHeight / 2f + verticalSpacing; } } // 动态修改总砖块数 public void UpdateTotalBricks(int count) { totalBrickCount = count; RefreshLayout(); } }
使用说明
- 将脚本挂载到ScrollRect的
Content对象上 - 拖入砖块预制体、Viewport对象,设置布局参数
- 运行后自动生成交错布局,调整Viewport尺寸会自动适配
- 如需支持无限滚动,将销毁砖块的逻辑替换为对象池回收,添加滚动监听实现动态加载
内容的提问来源于stack exchange,提问作者KillerFrog11
相关产品推荐
相关产品推荐

