Unity中基于Perlin噪声的GameObject集群生成异常问题
问题:Unity中Perlin噪声偏移后GameObject生成模式不匹配
我正在制作一款程序化生成游戏,尝试在Unity中使用Perlin噪声作为模板生成2D预制件的森林/集群。核心方案是生成Perlin噪声,利用噪声的暗色调区域生成类似森林的GameObject集群。
噪声生成时带有offsetX和offsetY参数,取值为0到999999之间的随机数,用于在范围内随机偏移噪声图案。当前噪声显示在Quad上,仅用于观察生成点与噪声的关联,最终会隐藏噪声只保留生成的GameObject。
核心问题:技术上功能可运行,但GameObject始终基于offsetX=0、offsetY=0时的噪声图案生成;每次噪声图案是随机变化的,但生成的GameObject模式却未随之改变——RandomObjectSpawner脚本未正确使用offsetX和offsetY来匹配对应的噪声“帧”(例如当offsetX=28904、offsetY=98201时,生成点仍和offset为0时的模式完全一致)。
现象说明
- offsetX和offsetY为0时:噪声纹理与生成的植物预制件位置完全对应
- offsetX和offsetY随机时:噪声纹理已呈现随机图案,但植物预制件的生成模式几乎和offset为0时一致
Perlin噪声生成代码
public class PerlinNoise : MonoBehaviour { // 纹理尺寸 public int width = 256; public int height = 256; // 控制Perlin噪声的缩放与偏移参数 public float scale = 20f; public float offsetX = 0f; public float offsetY = 0f; void Start() { // 随机化偏移值以生成不同噪声图案 offsetX = Random.Range(0f, 999999f); offsetY = Random.Range(0f, 999999f); } void Update() { // 获取挂载对象的Renderer组件 Renderer renderer = GetComponent<Renderer>(); // 将生成的纹理应用到对象材质 renderer.material.mainTexture = GenerateTexture(); } // 将GenerateTexture方法设为公开 public Texture2D GenerateTexture() { // 创建指定尺寸的新2D纹理 Texture2D texture = new Texture2D(width, height); // 为纹理生成Perlin噪声图 for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { // 使用Perlin噪声计算当前像素颜色 Color color = CalculateColor(x, y); // 设置纹理中该像素的颜色 texture.SetPixel(x, y, color); } } // 应用纹理修改 texture.Apply(); return texture; } Color CalculateColor(int x, int y) { // 计算纹理内的归一化坐标 float xCoord = (float)x / width * scale + offsetX; float yCoord = (float)y / height * scale + offsetY; // 在指定坐标生成Perlin噪声值 float sample = Mathf.PerlinNoise(xCoord, yCoord); // 使用噪声值创建灰度颜色 return new Color(sample, sample, sample); } }
GameObject生成代码
public class RandomObjectSpawner : MonoBehaviour { // 在暗色调区域实例化的预制件 public GameObject prefabToInstantiate; // PerlinNoise脚本引用 public PerlinNoise perlinNoiseScript; // 判断暗色调的阈值 public float threshold = 0.4f; // 生成对象的数量 public int numberOfObjects = 10; void Start() { // 确保Inspector中已赋值PerlinNoise脚本 if (perlinNoiseScript == null) { Debug.LogError("PerlinNoise script is not assigned."); return; } // 从PerlinNoise脚本获取offsetX和offsetY值 float offsetX = perlinNoiseScript.offsetX; float offsetY = perlinNoiseScript.offsetY; // 生成Perlin噪声纹理 Texture2D noiseTexture = perlinNoiseScript.GenerateTexture(); // 计算生成纹理的宽高 int width = noiseTexture.width; int height = noiseTexture.height; // 创建存储可用生成位置的列表 List<Vector2Int> availableSpawnPositions = new List<Vector2Int>(); // 遍历Perlin噪声纹理的像素,从offsetX和offsetY开始 for (int x = (int)perlinNoiseScript.offsetX; x < width; x++) { for (int y = (int)perlinNoiseScript.offsetY; y < height; y++) { // 获取当前像素的颜色 Color color = noiseTexture.GetPixel(x, y); // 检查灰度值是否低于阈值 if (color.grayscale <= threshold) { // 将位置添加到可用生成位置列表 availableSpawnPositions.Add(new Vector2Int(x, y)); } } } // 打乱可用生成位置列表以实现随机性 ShuffleList(availableSpawnPositions); // 从打乱后的列表中随机生成指定数量的对象 int spawnedObjectCount = 0; foreach (Vector2Int spawnPosition in availableSpawnPositions) { if (spawnedObjectCount >= numberOfObjects) break; // 根据纹理坐标计算对象的世界位置 float worldX = (spawnPosition.x - (int)perlinNoiseScript.offsetX) * perlinNoiseScript.transform.localScale.x / width + perlinNoiseScript.transform.position.x - (perlinNoiseScript.transform.localScale.x / 2.0f); float worldY = (spawnPosition.y - (int)perlinNoiseScript.offsetY) * perlinNoiseScript.transform.localScale.y / height + perlinNoiseScript.transform.position.y - (perlinNoiseScript.transform.localScale.y / 2.0f); float worldZ = perlinNoiseScript.transform.position.z; // 在计算出的位置生成对象 Instantiate(prefabToInstantiate, new Vector3(worldX, worldY, worldZ), Quaternion.identity); spawnedObjectCount++; } } // 打乱列表的函数 void ShuffleList<T>(List<T> list) { int n = list.Count; while (n > 1) { n--; int k = Random.Range(0, n + 1); T value = list[k]; list[k] = list[n]; list[n] = value; } } }
内容的提问来源于stack exchange,提问作者Tobias Thy
相关产品推荐
相关产品推荐

