创建无缝块生成时出现问题

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

我正在努力创建无限的地形块,使用统一的柏林噪声来改变高度,并且在使接缝对齐以创建可平铺环境时遇到问题问题屏幕截图

TerrainChunk.cs

using UnityEngine;
using System.Linq; // Add this line

public class TerrainChunk
{
    public GameObject chunkObject;
    private Terrain terrain;
    private TerrainData terrainData;
    private int width, height, depth;
    private float scale;
    private int seed;
    private Vector3 position;
    private TerrainLayer[] terrainLayers;
    private Material terrainMaterial;

    
    private GameObject npcPrefab;  // Add this line to hold the NPC prefab.

    private NPCSpawner npcSpawner; // Reference to the NPCSpawner component

    public TerrainChunk(int width, int height, int depth, float scale, int seed, Vector3 position, TerrainLayer[] terrainLayers, Material terrainMaterial, GameObject npcPrefab) // Add npcPrefab parameter
    {
        this.width = width;
        this.height = height;
        this.depth = depth;
        this.scale = scale;
        this.seed = seed;
        this.position = position;
        this.terrainLayers = terrainLayers;
        this.terrainMaterial = terrainMaterial;
        this.npcPrefab = npcPrefab; // Assign to field


        CreateTerrainChunk();
    }

    private void CreateTerrainChunk()
    {
        // Create a new GameObject for the terrain chunk
        chunkObject = new GameObject("TerrainChunk");
        chunkObject.transform.position = position;

        // Add Terrain component and create TerrainData
        terrain = chunkObject.AddComponent<Terrain>();
        terrainData = new TerrainData();
        terrain.terrainData = terrainData;

        // Set terrain data properties
        terrainData.heightmapResolution = width + 1;
        terrainData.size = new Vector3(width, depth, height);
        terrainData.SetHeights(0, 0, GenerateHeights());

        // Set terrain layers
        if (terrainLayers != null && terrainLayers.Length > 0)
        {
            terrainData.terrainLayers = terrainLayers;
        }

        // Generate and apply the splatmap
        GenerateAndApplySplatmap();

        // Assign the material to the terrain
        if (terrainMaterial != null)
        {
            terrain.materialTemplate = terrainMaterial;
        }
        else
        {
            Debug.LogError("No material assigned to terrain");
        }

        // Optionally, add a TerrainCollider
        chunkObject.AddComponent<TerrainCollider>().terrainData = terrainData;

        // Existing code for setting up terrain...

        // Find or Add an NPCSpawner component
        npcSpawner = chunkObject.GetComponent<NPCSpawner>() ?? chunkObject.AddComponent<NPCSpawner>();
        npcSpawner.npcPrefab = this.npcPrefab;  // Ensure the NPC spawner knows which prefab to use
        npcSpawner.TrySpawnNPCs(position, width, height);
    }

    private float[,] GenerateHeights()
    {
        float[,] heights = new float[width, height];
        for (int x = 0; x < width; x++)
        {
            for (int y = 0; y < height; y++)
            {
                heights[x, y] = CalculateHeight(x, y);
            }
        }
        return heights;
    }
    // This is the issue 
    private float CalculateHeight(int x, int y)
    {
        float xCoord = (position.x + x) / width * scale + seed * 0.0001f;
        float yCoord = (position.z + y) / height * scale + seed * 0.0001f;

        // Base layer
        float baseHeight = Mathf.PerlinNoise(xCoord, yCoord);
        return baseHeight;
    }

    private void GenerateAndApplySplatmap()
    {
        // Get the terrain size and heightmap
        int terrainWidth = terrainData.alphamapWidth;
        int terrainHeight = terrainData.alphamapHeight;
        int numTextures = terrainLayers.Length;

        // Create a new splatmap array
        float[,,] splatmap = new float[terrainWidth, terrainHeight, numTextures];

        // Loop through all terrain points
        for (int x = 0; x < terrainWidth; x++)
        {
            for (int y = 0; y < terrainHeight; y++)
            {
                // Normalized coordinates
                float xCoord = (float)x / (terrainWidth - 1);
                float yCoord = (float)y / (terrainHeight - 1);

                // Get the height at this point
                float heightAtPoint = terrainData.GetHeight((int)(xCoord * terrainData.heightmapResolution), (int)(yCoord * terrainData.heightmapResolution));

                // Calculate weights based on height
                float[] weights = new float[numTextures];
                for (int i = 0; i < numTextures; i++)
                {
                    // Simple gradient based on height for this example
                    if (i == 0)
                    {
                        weights[i] = Mathf.Clamp01(1 - heightAtPoint / depth); // Low areas (e.g., grass)
                    }
                    else if (i == 1)
                    {
                        weights[i] = Mathf.Clamp01((heightAtPoint / depth - 0.3f) * 2); // Mid areas (e.g., rock)
                    }
                    else if (i == 2)
                    {
                        weights[i] = Mathf.Clamp01((heightAtPoint / depth - 0.6f) * 3); // High areas (e.g., snow)
                    }
                }

                // Normalize weights
                float totalWeight = weights.Sum();
                for (int i = 0; i < numTextures; i++)
                {
                    weights[i] /= totalWeight;
                    splatmap[x, y, i] = weights[i];
                }
            }
        }

        // Assign the splatmap to the terrain
        terrainData.SetAlphamaps(0, 0, splatmap);
    }

    public void Unload()
    {
        // Destroy the GameObject and cleanup
        GameObject.Destroy(chunkObject);
        terrain = null;
        terrainData = null;
    }

    public bool IsWithinViewDistance(Vector2Int playerChunkCoord, int viewDistance)
    {
        Vector2Int chunkCoord = new Vector2Int(
            Mathf.FloorToInt(position.x / width),
            Mathf.FloorToInt(position.z / height)
        );

        int distance = Mathf.Max(Mathf.Abs(playerChunkCoord.x - chunkCoord.x), Mathf.Abs(playerChunkCoord.y - chunkCoord.y));
        return distance <= viewDistance;
    }
}

地形管理器.cs

using System.Collections.Generic;
using UnityEngine;


public class TerrainManager : MonoBehaviour
{
    public int chunkSize = 256;
    public int chunkDepth = 20;
    public float scale = 20f;
    public int seed;
    public TerrainLayer[] terrainLayers;
    public Material terrainMaterial;

    private Dictionary<Vector2Int, TerrainChunk> terrainChunks = new Dictionary<Vector2Int, TerrainChunk>();
    private Transform player;
    private int viewDistance = 2; // Number of chunks to load around the player

    [SerializeField] private GameObject npcPrefab; // Add this line

    void Start()
    {
        // Set the seed for the terrain
        if (seed == 0)
        {
            seed = Random.Range(0, int.MaxValue);
        }

        // Get player transform
        player = GameObject.FindGameObjectWithTag("Player").transform;

        // Generate initial terrain
        UpdateTerrainChunks();
    }

    void Update()
    {
        // Update terrain chunks based on player position
        UpdateTerrainChunks();
    }

    private void UpdateTerrainChunks()
    {
        Vector2Int playerChunkCoord = new Vector2Int(
            Mathf.FloorToInt(player.position.x / chunkSize),
            Mathf.FloorToInt(player.position.z / chunkSize)
        );

        GameObject npcPrefab = Resources.Load<GameObject>("NPC"); // Replace "NameOfYourPrefab" with the actual name

        List<Vector2Int> chunksToRemove = new List<Vector2Int>();

        // Check and unload distant chunks
        foreach (var chunkCoord in terrainChunks.Keys)
        {
            if (!IsChunkWithinViewDistance(chunkCoord, playerChunkCoord))
            {
                terrainChunks[chunkCoord].Unload();
                chunksToRemove.Add(chunkCoord);
            }
        }

        // Remove unloaded chunks from dictionary
        foreach (var chunkCoord in chunksToRemove)
        {
            terrainChunks.Remove(chunkCoord);
        }

        // Load new chunks around the player
        for (int xOffset = -viewDistance; xOffset <= viewDistance; xOffset++)
        {
            for (int yOffset = -viewDistance; yOffset <= viewDistance; yOffset++)
            {
                Vector2Int chunkCoord = new Vector2Int(playerChunkCoord.x + xOffset, playerChunkCoord.y + yOffset);

                if (!terrainChunks.ContainsKey(chunkCoord))
                {
                    Vector3 chunkPosition = new Vector3(chunkCoord.x * chunkSize, 0, chunkCoord.y * chunkSize);
                    TerrainChunk newChunk = new TerrainChunk(chunkSize, chunkSize, chunkDepth, scale, seed, chunkPosition, terrainLayers, terrainMaterial, npcPrefab);
                    terrainChunks.Add(chunkCoord, newChunk);
                }
            }
        }
    }

    private bool IsChunkWithinViewDistance(Vector2Int chunkCoord, Vector2Int playerChunkCoord)
    {
        int distance = Mathf.Max(Mathf.Abs(playerChunkCoord.x - chunkCoord.x), Mathf.Abs(playerChunkCoord.y - chunkCoord.y));
        return distance <= viewDistance;
    }
}

我尝试找到问题的根源,并通过无缝生成来研究其他人的问题,但很难将它们实现到我的代码中。

c# unity-game-engine procedural-generation
1个回答
0
投票

从图像上很难看出。但是,您确定地形的 x 和 y 方向实际上与块/世界的 x 和 y 方向匹配吗?我的猜测是,您可能有一个旋转的地形(因此本地原点 x==0,y==0 不是块的下角)。因此请确保您的

heights[0,0]
的位置实际上位于 x-z 世界网格中的较低位置

© www.soinside.com 2019 - 2024. All rights reserved.