我正在努力创建无限的地形块,使用统一的柏林噪声来改变高度,并且在使接缝对齐以创建可平铺环境时遇到问题问题屏幕截图
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;
}
}
我尝试找到问题的根源,并通过无缝生成来研究其他人的问题,但很难将它们实现到我的代码中。
从图像上很难看出。但是,您确定地形的 x 和 y 方向实际上与块/世界的 x 和 y 方向匹配吗?我的猜测是,您可能有一个旋转的地形(因此本地原点 x==0,y==0 不是块的下角)。因此请确保您的
heights[0,0]
的位置实际上位于 x-z 世界网格中的较低位置