如何按特定顺序基于黑白图在3D场景中生成物体
实现特定顺序遍历像素生成物体的方案
核心思路
要按特定顺序生成物体,本质是修改像素遍历的循环逻辑——不再用固定的x从左到右、y从下到上的嵌套循环,而是先按目标顺序生成所有像素坐标的序列,再依次遍历这个序列判断是否生成物体。
常见特定顺序的实现方法
1. 蛇形遍历(左右交替逐行)
如果目标是逐行扫描,奇数行从左到右、偶数行从右到左(类似蛇形路径),可以直接修改循环方向:
using Dreamteck.Splines; using System.Collections.Generic; using UnityEngine; [ExecuteInEditMode] public class ReadImage : MonoBehaviour { public Texture2D spawnMap; public float spawnSpacing = 1.0f; public Vector2 startPosition = Vector2.zero; public bool canSpawn; [SerializeField] GameObject cubePrefab; private void OnEnable() { if (canSpawn) { SpawnObjects(); } } void SpawnObjects() { // 先清除已有生成物体,避免重复 foreach (Transform child in transform) DestroyImmediate(child); int index = 0; for (int y = 0; y < spawnMap.height; y++) { // 偶数行从左到右,奇数行从右到左 if (y % 2 == 0) { for (int x = 0; x < spawnMap.width; x++) { SpawnIfWhite(x, y, ref index); } } else { for (int x = spawnMap.width - 1; x >= 0; x--) { SpawnIfWhite(x, y, ref index); } } } } // 提取重复逻辑,简化代码 void SpawnIfWhite(int x, int y, ref int index) { Color pixelColor = spawnMap.GetPixel(x, y); if (pixelColor == Color.white) { Vector3 worldPos = new Vector3(x * spawnSpacing, 0, y * spawnSpacing); index++; Instantiate(cubePrefab, worldPos, Quaternion.identity, transform); } } }
2. 螺旋形遍历
如果目标是从角落或中心向外螺旋扫描,需要先生成螺旋路径的坐标列表,再遍历生成:
// 仅修改SpawnObjects和新增GenerateSpiralCoords方法,其余代码同上 void SpawnObjects() { foreach (Transform child in transform) DestroyImmediate(child); int index = 0; List<Vector2Int> spiralCoords = GenerateSpiralCoords(spawnMap.width, spawnMap.height); foreach (var coord in spiralCoords) { SpawnIfWhite(coord.x, coord.y, ref index); } } List<Vector2Int> GenerateSpiralCoords(int width, int height) { List<Vector2Int> coords = new List<Vector2Int>(); int left = 0, right = width - 1; int top = height - 1, bottom = 0; while (left <= right && bottom <= top) { // 从左到右遍历顶部行 for (int x = left; x <= right; x++) coords.Add(new Vector2Int(x, top)); top--; // 从上到下遍历右侧列 for (int y = top; y >= bottom; y--) coords.Add(new Vector2Int(right, y)); right--; // 从右到左遍历底部行(剩余行有效时) if (bottom <= top) { for (int x = right; x >= left; x--) coords.Add(new Vector2Int(x, bottom)); bottom++; } // 从下到上遍历左侧列(剩余列有效时) if (left <= right) { for (int y = bottom; y <= top; y++) coords.Add(new Vector2Int(left, y)); left++; } } return coords; }
3. 自定义路径(匹配参考图的特定顺序)
如果参考图的顺序是完全自定义的(比如特定折线、区域优先),可以用灰度图标记顺序:
- 把参考图改成灰度图:路径上的像素灰度从0(最先)到255(最后)渐变
- 按灰度值排序像素坐标,再遍历生成:
// 仅修改SpawnObjects方法,其余代码同上 void SpawnObjects() { foreach (Transform child in transform) DestroyImmediate(child); int index = 0; List<(Vector2Int coord, float orderValue)> pixelOrder = new List<(Vector2Int, float)>(); // 遍历所有像素,记录坐标和对应的灰度值(灰度越小越先) for (int x = 0; x < spawnMap.width; x++) { for (int y = 0; y < spawnMap.height; y++) { Color pixelColor = spawnMap.GetPixel(x, y); float grayValue = (pixelColor.r + pixelColor.g + pixelColor.b) / 3f; pixelOrder.Add((new Vector2Int(x, y), grayValue)); } } // 按灰度值从小到大排序,匹配参考图顺序 pixelOrder.Sort((a, b) => a.orderValue.CompareTo(b.orderValue)); // 遍历排序后的列表生成物体 foreach (var item in pixelOrder) { Color pixelColor = spawnMap.GetPixel(item.coord.x, item.coord.y); if (pixelColor == Color.white) { Vector3 worldPos = new Vector3(item.coord.x * spawnSpacing, 0, item.coord.y * spawnSpacing); index++; Instantiate(cubePrefab, worldPos, Quaternion.identity, transform); } } }
注意事项
- 若需要完全匹配参考图的自定义路径,优先用第三种灰度图标记的方法,精度最高;
- EditMode下运行时,务必先清除已有生成物体,避免重复实例化;
- 判断白色像素时可以加容差(比如
pixelColor.grayscale > 0.9f),避免因图像压缩导致的纯白判断失效。
内容的提问来源于stack exchange,提问作者Sachin Kankane
相关产品推荐
相关产品推荐

