Unity 程序生成的游戏对象未按所需方式生成

问题描述 投票:0回答:1

我正在尝试使用 Perlin 噪声作为统一生成森林/2D 预制件簇的模式,用于我的程序生成游戏。所以我的计划是生成 Perlin 噪声,然后使用 Perlin 噪声的暗色调生成看起来像森林的游戏对象簇。 生成的噪声具有 offsetX 和 offsetY 值,该值是 0 到 999999 之间的随机数,用于随机抵消帧内生成的噪声。噪声显示在四边形中,但完成后我将仅使用游戏对象,而不使用它们下面的噪声。现在我只有可见的噪声,这样我就可以看到生成点和噪声之间的相关性。 所以问题是,现在,它在技术上是有效的,但是游戏对象是根据偏移X 0和偏移Y 0处的帧中的噪声生成的,但是噪声每次都是随机的,但不是生成的游戏对象,所以对于某些原因是,RandomObjectSpawner 脚本不使用 offsetX 和 offsetY 值来计算,告诉它它是哪个噪声“帧”,(例如)在 offsetX 28904、offsetY 98201 噪声种子处,它仍然会生成以下模式中的点:偏移 0 噪声位置。 将提供示例屏幕截图。

这是 Noise and Generated GameObjects 的图像,位于 offsetX 0 和 offsetY 0 处

这是 offsetX and offsetY values are random 的时候: 正如您在图 2 中看到的,小占位符测试植物的生成方式与第一张图片几乎相同。所以我需要它们始终以随机生成的 offsetX 和 offsetY 值的模式生成。

以下是我生成柏林噪声的代码: `公共类 PerlinNoise :MonoBehaviour { // 纹理尺寸 公共整数宽度= 256; 公共 int 高度 = 256;

// Scale and offset parameters for controlling Perlin noise
public float scale = 20f;
public float offsetX = 0f;
public float offsetY = 0f;

void Start()
{
    // Randomize the offsets to create different noise patterns
    offsetX = Random.Range(0f, 999999f);
    offsetY = Random.Range(0f, 999999f);
}

void Update()
{
    // Get the renderer component of the attached GameObject
    Renderer renderer = GetComponent<Renderer>();
    // Apply the generated texture to the object's material
    renderer.material.mainTexture = GenerateTexture();
}

// Make the GenerateTexture method public
public Texture2D GenerateTexture()
{
    // Create a new 2D texture with specified dimensions
    Texture2D texture = new Texture2D(width, height);

    // Generate a Perlin noise map for the texture
    for (int x = 0; x < width; x++)
    {
        for (int y = 0; y < height; y++)
        {
            // Calculate the color for the current pixel using Perlin noise
            Color color = CalculateColor(x, y);
            // Set the pixel's color in the texture
            texture.SetPixel(x, y, color);
        }
    }
    // Apply changes to the texture
    texture.Apply();
    return texture;
}

Color CalculateColor(int x, int y)
{
    // Calculate the normalized coordinates within the texture
    float xCoord = (float)x / width * scale + offsetX;
    float yCoord = (float)y / height * scale + offsetY;

    // Generate a Perlin noise value at the given coordinates
    float sample = Mathf.PerlinNoise(xCoord, yCoord);

    // Create a grayscale color using the Perlin noise value
    return new Color(sample, sample, sample);
}
}`

这是我在游戏对象中生成的代码: `公共类 RandomObjectSpawner : MonoBehaviour { // 在暗色调上实例化的预制件 公共游戏对象预制实例化;

// Reference to the PerlinNoise script
public PerlinNoise perlinNoiseScript;

// Threshold to determine dark tones
public float threshold = 0.4f;

// Number of objects to spawn
public int numberOfObjects = 10;

void Start() {
    // Ensure that the PerlinNoise script is assigned in the Inspector
    if (perlinNoiseScript == null) {
        Debug.LogError("PerlinNoise script is not assigned.");
        return;
    }

    // Get the offsetX and offsetY values from the PerlinNoise script
    float offsetX = perlinNoiseScript.offsetX;
    float offsetY = perlinNoiseScript.offsetY;

    // Generate the Perlin noise texture
    Texture2D noiseTexture = perlinNoiseScript.GenerateTexture();

    // Calculate the width and height of the generated texture
    int width = noiseTexture.width;
    int height = noiseTexture.height;

    // Create a list to store available spawn positions
    List<Vector2Int> availableSpawnPositions = new List<Vector2Int>();

    // Loop through the pixels of the Perlin noise texture, starting from offsetX and offsetY
    for (int x = (int)perlinNoiseScript.offsetX; x < width; x++) {
        for (int y = (int)perlinNoiseScript.offsetY; y < height; y++) {
            // Get the color at the current pixel
            Color color = noiseTexture.GetPixel(x, y);

            // Check if the grayscale value is below the threshold
            if (color.grayscale <= threshold) {
                // Add the position to the list of available spawn positions
                availableSpawnPositions.Add(new Vector2Int(x, y));
            }
        }
    }

    // Shuffle the list of available spawn positions for randomness
    ShuffleList(availableSpawnPositions);

    // Spawn the specified number of objects randomly from the shuffled list
    int spawnedObjectCount = 0;
    foreach (Vector2Int spawnPosition in availableSpawnPositions) {
        if (spawnedObjectCount >= numberOfObjects)
            break;

        // Calculate the world position for the object based on texture coordinates
        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;

        // Spawn the object at the calculated position
        Instantiate(prefabToInstantiate, new Vector3(worldX, worldY, worldZ), Quaternion.identity);

        spawnedObjectCount++;
    }
}

// Function to shuffle a list
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;
    }
}
}`

提前致谢。 :)

c# unity-game-engine 2d procedural-generation perlin-noise
1个回答
0
投票

由于您在 Start() 中运行随机发生器,

void Start()
{
    // Randomize the offsets to create different noise patterns
    offsetX = Random.Range(0f, 999999f);
    offsetY = Random.Range(0f, 999999f);
}

同时在Start()中设置柏林噪声纹理

    void Start() {
    if (perlinNoiseScript == null) {
        Debug.LogError("PerlinNoise script is not assigned.");
        return;
    }

    //Race condition here
    float offsetX = perlinNoiseScript.offsetX;
    float offsetY = perlinNoiseScript.offsetY;

您最终会尝试同时获取 perlinNoiseScript.offsetX,而噪声脚本仍在随机化数字。 Unity 通过让类返回在运行任何 Awake() 方法之后但在调用任何 Start() 方法之前存在的值来防止在这些函数期间发生竞争条件。因此,您得到的是输入检查器的数字,而不是 Start() 期间设置的数字。

要解决此问题,只需将偏移随机化器代码移至 Awake() 函数中,该函数在帧中启动任何 Start() 函数之前运行。

void Awake()
{
// Randomize the offsets to create different noise patterns
    offsetX = Random.Range(0f, 999999f);
    offsetY = Random.Range(0f, 999999f);
}
© www.soinside.com 2019 - 2024. All rights reserved.