我正在按照 b3agz 的 Make Minecraft In Unity 3D 教程制作类似 minecraft 的游戏,我正在学习第 11 章 - 树,但我的代码使我的游戏崩溃。这里有很多代码只是为了让你们知道。本教程相当古老(准确地说是 3-4 年),所以它可能只是代码,可能不是,因为我使用的是最新的 unity 3d 版本,其他一切都运行良好。当我点击播放按钮时,我的整体崩溃了,我不得不再次打开它。这是 world.cs :
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class World : MonoBehaviour {
public int seed;
public BiomeAttributes biome;
public Transform player;
public Vector3 spawnPosition;
public Material material;
public Material transparentMaterial;
public BlockType[] blockTypes;
Chunk[,] chunks = new Chunk[VoxelData.WorldSizeInChunks, VoxelData.WorldSizeInChunks];
List<ChunkCoord> activeChunks = new List<ChunkCoord>();
public ChunkCoord playerChunkCoord;
ChunkCoord playerLastChunkCoord;
List<ChunkCoord> chunksToCreate = new List<ChunkCoord>();
List<Chunk> chunksToUpdate = new List<Chunk>();
bool applyingModifications = false;
Queue<VoxelMod> modifications = new Queue<VoxelMod>();
public GameObject debugScreen;
private void Start() {
Random.InitState(seed);
spawnPosition = new Vector3((VoxelData.WorldSizeInChunks * VoxelData.ChunkWidth) / 2f, VoxelData.ChunkHeight - 50f, (VoxelData.WorldSizeInChunks * VoxelData.ChunkWidth) / 2f);
GenerateWorld();
playerLastChunkCoord = GetChunkCoordFromVector3(player.position);
}
private void Update() {
playerChunkCoord = GetChunkCoordFromVector3(player.position);
if (!playerChunkCoord.Equals(playerLastChunkCoord))
CheckRenderDistance();
if(modifications.Count > 0 && !applyingModifications)
StartCoroutine(ApplyModifications());
if(chunksToCreate.Count > 0)
CreateChunk();
if(chunksToUpdate.Count > 0)
UpdateChunks();
if (Input.GetKeyDown(KeyCode.F3))
debugScreen.SetActive(!debugScreen.activeSelf);
}
void GenerateWorld () {
for (int x = (VoxelData.WorldSizeInChunks / 2) - VoxelData.RenderDistanceInChunks; x < (VoxelData.WorldSizeInChunks / 2) + VoxelData.RenderDistanceInChunks; x++) {
for (int z = (VoxelData.WorldSizeInChunks / 2) - VoxelData.RenderDistanceInChunks; z < (VoxelData.WorldSizeInChunks / 2) + VoxelData.RenderDistanceInChunks; z++) {
chunks[x, z] = new Chunk(new ChunkCoord(x, z), this, true);
activeChunks.Add(new ChunkCoord(x, z));
}
}
while(modifications.Count > 0) {
VoxelMod v = modifications.Dequeue();
ChunkCoord c = GetChunkCoordFromVector3(v.position);
if(chunks[c.x, c.z] == null) {
chunks[c.x, c.z] = new Chunk(c, this, true);
activeChunks.Add(c);
}
chunks[c.x, c.z].modifications.Enqueue(v);
if(!chunksToUpdate.Contains(chunks[c.x, c.z]))
chunksToUpdate.Add(chunks[c.x, c.z]);
}
for (int i = 0; i < chunksToUpdate.Count; i++) {
chunksToUpdate[0].UpdateChunk();
chunksToUpdate.RemoveAt(0);
}
player.position = spawnPosition;
}
void CreateChunk () {
ChunkCoord c = chunksToCreate[0];
chunksToCreate.RemoveAt(0);
activeChunks.Add(c);
chunks[c.x, c.z].Init();
}
void UpdateChunks () {
bool updated = false;
int index = 0;
while (!updated && index < chunksToUpdate.Count - 1) {
if (chunksToUpdate[index].isVoxelMapPopulated) {
chunksToUpdate[index].UpdateChunk();
chunksToUpdate.RemoveAt(index);
updated = true;
}
else
index++;
}
}
IEnumerator ApplyModifications () {
applyingModifications = true;
int count = 0;
while (modifications.Count > 0) {
VoxelMod v = modifications.Dequeue();
ChunkCoord c = GetChunkCoordFromVector3(v.position);
if (chunks[c.x, c.z] == null) {
chunks[c.x, c.z] = new Chunk(c, this, true);
activeChunks.Add(c);
}
chunks[c.x, c.z].modifications.Enqueue(v);
if (!chunksToUpdate.Contains(chunks[c.x, c.z]))
chunksToUpdate.Add(chunks[c.x, c.z]);
count++;
if (count > 110) {
count = 0;
yield return null;
}
}
applyingModifications = false;
}
ChunkCoord GetChunkCoordFromVector3 (Vector3 pos) {
int x = Mathf.FloorToInt(pos.x / VoxelData.ChunkWidth);
int z = Mathf.FloorToInt(pos.z / VoxelData.ChunkWidth);
return new ChunkCoord(x, z);
}
public Chunk GetChunkFromVector3 (Vector3 pos) {
int x = Mathf.FloorToInt(pos.x / VoxelData.ChunkWidth);
int z = Mathf.FloorToInt(pos.z / VoxelData.ChunkWidth);
return chunks[x, z];
}
void CheckRenderDistance () {
ChunkCoord coord = GetChunkCoordFromVector3(player.position);
playerLastChunkCoord = playerChunkCoord;
List<ChunkCoord> previouslyActiveChunks = new List<ChunkCoord>(activeChunks);
for (int x = coord.x - VoxelData.RenderDistanceInChunks; x < coord.x + VoxelData.RenderDistanceInChunks; x++) {
for (int z = coord.z - VoxelData.RenderDistanceInChunks; z < coord.z + VoxelData.RenderDistanceInChunks; z++) {
if (IsChunkInWorld (new ChunkCoord (x, z))) {
if (chunks[x, z] == null) {
chunks[x, z] = new Chunk(new ChunkCoord(x, z), this, false);
chunksToCreate.Add(new ChunkCoord(x, z));
} else if (!chunks[x, z].isActive) {
chunks[x, z].isActive = true;
}
activeChunks.Add(new ChunkCoord(x, z));
}
// Check through previously active chunks to see if this chunk is there. If it is, remove it from the list.
for (int i = 0; i < previouslyActiveChunks.Count; i++) {
if (previouslyActiveChunks[i].Equals(new ChunkCoord(x, z)))
previouslyActiveChunks.RemoveAt(i);
}
}
}
// Any chunks left in the previousActiveChunks list are no longer in the player's view distance, so loop through and disable them.
foreach (ChunkCoord c in previouslyActiveChunks)
chunks[c.x, c.z].isActive = false;
}
public bool CheckForVoxel (Vector3 pos) {
ChunkCoord thisChunk = new ChunkCoord(pos);
if (!IsChunkInWorld(thisChunk) || pos.y < 0 || pos.y > VoxelData.ChunkHeight)
return false;
if (chunks[thisChunk.x, thisChunk.z] != null && chunks[thisChunk.x, thisChunk.z].isVoxelMapPopulated)
return blockTypes[chunks[thisChunk.x, thisChunk.z].GetVoxelFromGlobalVector3(pos)].isSolid;
return blockTypes[GetVoxel(pos)].isSolid;
}
public bool CheckIfVoxelTransparent (Vector3 pos) {
ChunkCoord thisChunk = new ChunkCoord(pos);
if (!IsChunkInWorld(thisChunk) || pos.y < 0 || pos.y > VoxelData.ChunkHeight)
return false;
if (chunks[thisChunk.x, thisChunk.z] != null && chunks[thisChunk.x, thisChunk.z].isVoxelMapPopulated)
return blockTypes[chunks[thisChunk.x, thisChunk.z].GetVoxelFromGlobalVector3(pos)].isTransparent;
return blockTypes[GetVoxel(pos)].isTransparent;
}
public byte GetVoxel (Vector3 pos) {
int yPos = Mathf.FloorToInt(pos.y);
/* IMMUTABLE PASS */
// If outside world, return air.
if (!IsVoxelInWorld(pos))
return 0;
// If bottom block of chunk, return bedrock.
if (yPos == 0)
return 1;
/* BASIC TERRAIN PASS */
int terrainHeight = Mathf.FloorToInt(biome.terrainHeight * Noise.Get2DPerlin(new Vector2(pos.x, pos.z), 0, biome.terrainScale)) + biome.solidGroundHeight;
byte voxelValue = 0;
if (yPos == terrainHeight)
voxelValue = 3;
else if (yPos < terrainHeight && yPos > terrainHeight - 4)
voxelValue = 5;
else if (yPos > terrainHeight)
return 0;
else
voxelValue = 2;
/* SECOND PASS */
if (voxelValue == 2) {
foreach (Lode lode in biome.lodes) {
if (yPos > lode.minHeight && yPos < lode.maxHeight)
if (Noise.Get3DPerlin(pos, lode.noiseOffset, lode.scale, lode.threshold))
voxelValue = lode.blockID;
}
}
/* TREE PASS */
if (yPos == terrainHeight) {
if (Noise.Get2DPerlin(new Vector2(pos.x, pos.z), 0f, biome.treeZoneScale) > biome.treeZoneThreshold) {
voxelValue = 1;
if (Noise.Get2DPerlin(new Vector2(pos.x, pos.z), 0f, biome.treePlacementScale) > biome.treePlacementThreshold)
voxelValue = 8;
Structure.MakeTree(pos, modifications, biome.minTreeHeight, biome.maxTreeHeight);
}
}
return voxelValue;
}
bool IsChunkInWorld (ChunkCoord coord) {
if (coord.x > 0 && coord.x < VoxelData.WorldSizeInChunks - 1 && coord.z > 0 && coord.z < VoxelData.WorldSizeInChunks - 1)
return true;
else
return
false;
}
bool IsVoxelInWorld (Vector3 pos) {
if (pos.x >= 0 && pos.x < VoxelData.WorldSizeInVoxels && pos.y >= 0 && pos.y < VoxelData.ChunkHeight && pos.z >= 0 && pos.z < VoxelData.WorldSizeInVoxels)
return true;
else
return false;
}
}
[System.Serializable]
public class BlockType {
public string blockName;
public bool isSolid;
public bool isTransparent;
public Sprite icon;
[Header("Texture Values")]
public int backFaceTexture;
public int frontFaceTexture;
public int topFaceTexture;
public int bottomFaceTexture;
public int leftFaceTexture;
public int rightFaceTexture;
// Back, Front, Top, Bottom, Left, Right
public int GetTextureID (int faceIndex) {
switch (faceIndex) {
case 0:
return backFaceTexture;
case 1:
return frontFaceTexture;
case 2:
return topFaceTexture;
case 3:
return bottomFaceTexture;
case 4:
return leftFaceTexture;
case 5:
return rightFaceTexture;
default:
Debug.Log("Error in GetTextureID; invalid face index");
return 0;
}
}
}
public class VoxelMod {
public Vector3 position;
public byte id;
public VoxelMod () {
position = new Vector3();
id = 0;
}
public VoxelMod (Vector3 _position, byte _id) {
position = _position;
id = _id;
}
}
这是 chunk.cs:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Chunk {
public ChunkCoord coord;
GameObject chunkObject;
MeshRenderer meshRenderer;
MeshFilter meshFilter;
int vertexIndex = 0;
List<Vector3> vertices = new List<Vector3> ();
List<int> triangles = new List<int> ();
List<int> transparentTriangles = new List<int>();
Material[] materials = new Material[2];
List<Vector2> uvs = new List<Vector2> ();
public byte[,,] voxelMap = new byte[VoxelData.ChunkWidth, VoxelData.ChunkHeight, VoxelData.ChunkWidth];
public Queue<VoxelMod> modifications = new Queue<VoxelMod>();
World world;
private bool _isActive;
public bool isVoxelMapPopulated = false;
public Chunk (ChunkCoord _coord, World _world, bool generateOnLoad) {
coord = _coord;
world = _world;
isActive = true;
if (generateOnLoad)
Init();
}
public void Init () {
chunkObject = new GameObject();
meshFilter = chunkObject.AddComponent<MeshFilter>();
meshRenderer = chunkObject.AddComponent<MeshRenderer>();
materials[0] = world.material;
materials[1] = world.transparentMaterial;
meshRenderer.materials = materials;
chunkObject.transform.SetParent(world.transform);
chunkObject.transform.position = new Vector3(coord.x * VoxelData.ChunkWidth, 0f, coord.z * VoxelData.ChunkWidth);
chunkObject.name = "Chunk " + coord.x + ", " + coord.z;
PopulateVoxelMap();
UpdateChunk();
}
void PopulateVoxelMap () {
for (int y = 0; y < VoxelData.ChunkHeight; y++) {
for (int x = 0; x < VoxelData.ChunkWidth; x++) {
for (int z = 0; z < VoxelData.ChunkWidth; z++) {
voxelMap[x, y, z] = world.GetVoxel(new Vector3(x, y, z) + position);
}
}
}
isVoxelMapPopulated = true;
}
public void UpdateChunk () {
while (modifications.Count > 0) {
VoxelMod v = modifications.Dequeue();
Vector3 pos = v.position -= position;
voxelMap[(int)pos.x, (int)pos.y, (int)pos.z] = v.id;
}
ClearMeshData();
for (int y = 0; y < VoxelData.ChunkHeight; y++) {
for (int x = 0; x < VoxelData.ChunkWidth; x++) {
for (int z = 0; z < VoxelData.ChunkWidth; z++) {
if (world.blockTypes[voxelMap[x, y, z]].isSolid)
UpdateMeshData (new Vector3(x, y, z));
}
}
}
CreateMesh();
}
void ClearMeshData () {
vertexIndex = 0;
vertices.Clear();
triangles.Clear();
transparentTriangles.Clear();
uvs.Clear();
}
public bool isActive {
get { return _isActive; }
set {
_isActive = value;
if (chunkObject != null)
chunkObject.SetActive(value);
}
}
public Vector3 position {
get { return chunkObject.transform.position; }
}
bool IsVoxelInChunk (int x, int y, int z) {
if (x < 0 || x > VoxelData.ChunkWidth - 1 || y < 0 || y > VoxelData.ChunkHeight - 1 || z < 0 || z > VoxelData.ChunkWidth - 1)
return false;
else
return true;
}
public void EditVoxel (Vector3 pos, byte newID) {
int xCheck = Mathf.FloorToInt(pos.x);
int yCheck = Mathf.FloorToInt(pos.y);
int zCheck = Mathf.FloorToInt(pos.z);
xCheck -= Mathf.FloorToInt(chunkObject.transform.position.x);
zCheck -= Mathf.FloorToInt(chunkObject.transform.position.z);
voxelMap[xCheck, yCheck, zCheck] = newID;
UpdateSurroundingVoxels(xCheck, yCheck, zCheck);
UpdateChunk();
}
void UpdateSurroundingVoxels (int x, int y, int z) {
Vector3 thisVoxel = new Vector3(x, y, z);
for (int p = 0; p < 6; p++) {
Vector3 currentVoxel = thisVoxel + VoxelData.faceChecks[p];
if (!IsVoxelInChunk((int)currentVoxel.x, (int)currentVoxel.y, (int)currentVoxel.z)) {
world.GetChunkFromVector3(currentVoxel + position).UpdateChunk();
}
}
}
bool CheckVoxel (Vector3 pos) {
int x = Mathf.FloorToInt(pos.x);
int y = Mathf.FloorToInt(pos.y);
int z = Mathf.FloorToInt(pos.z);
if (!IsVoxelInChunk(x, y, z))
return world.CheckIfVoxelTransparent(pos + position);
return world.blockTypes[voxelMap[x, y, z]].isTransparent;
}
public byte GetVoxelFromGlobalVector3 (Vector3 pos) {
int xCheck = Mathf.FloorToInt(pos.x);
int yCheck = Mathf.FloorToInt(pos.y);
int zCheck = Mathf.FloorToInt(pos.z);
xCheck -= Mathf.FloorToInt(chunkObject.transform.position.x);
zCheck -= Mathf.FloorToInt(chunkObject.transform.position.z);
return voxelMap[xCheck, yCheck, zCheck];
}
void UpdateMeshData (Vector3 pos) {
byte blockID = voxelMap[(int)pos.x, (int)pos.y, (int)pos.z];
bool isTransparent = world.blockTypes[blockID].isTransparent;
for (int p = 0; p < 6; p++) {
if (CheckVoxel(pos + VoxelData.faceChecks[p])) {
vertices.Add (pos + VoxelData.voxelVerts [VoxelData.voxelTris [p, 0]]);
vertices.Add (pos + VoxelData.voxelVerts [VoxelData.voxelTris [p, 1]]);
vertices.Add (pos + VoxelData.voxelVerts [VoxelData.voxelTris [p, 2]]);
vertices.Add (pos + VoxelData.voxelVerts [VoxelData.voxelTris [p, 3]]);
AddTexture(world.blockTypes[blockID].GetTextureID(p));
if (!isTransparent) {
triangles.Add (vertexIndex);
triangles.Add (vertexIndex + 1);
triangles.Add (vertexIndex + 2);
triangles.Add (vertexIndex + 2);
triangles.Add (vertexIndex + 1);
triangles.Add (vertexIndex + 3);
} else {
transparentTriangles.Add (vertexIndex);
transparentTriangles.Add (vertexIndex + 1);
transparentTriangles.Add (vertexIndex + 2);
transparentTriangles.Add (vertexIndex + 2);
transparentTriangles.Add (vertexIndex + 1);
transparentTriangles.Add (vertexIndex + 3);
}
vertexIndex += 4;
}
}
}
void CreateMesh () {
Mesh mesh = new Mesh ();
mesh.vertices = vertices.ToArray ();
mesh.subMeshCount = 2;
mesh.SetTriangles(triangles.ToArray(), 0);
mesh.SetTriangles(transparentTriangles.ToArray(), 1);
mesh.uv = uvs.ToArray ();
mesh.RecalculateNormals ();
meshFilter.mesh = mesh;
}
void AddTexture (int textureID) {
float y = textureID / VoxelData.TextureAtlasSizeInBlocks;
float x = textureID - (y * VoxelData.TextureAtlasSizeInBlocks);
x *= VoxelData.NormalizedBlockTextureSize;
y *= VoxelData.NormalizedBlockTextureSize;
y = 1f - y - VoxelData.NormalizedBlockTextureSize;
uvs.Add(new Vector2(x, y));
uvs.Add(new Vector2(x, y + VoxelData.NormalizedBlockTextureSize));
uvs.Add(new Vector2(x + VoxelData.NormalizedBlockTextureSize, y));
uvs.Add(new Vector2(x + VoxelData.NormalizedBlockTextureSize, y + VoxelData.NormalizedBlockTextureSize));
}
}
public class ChunkCoord {
public int x;
public int z;
public ChunkCoord () {
x = 0;
z = 0;
}
public ChunkCoord (int _x, int _z) {
x = _x;
z = _z;
}
public ChunkCoord (Vector3 pos) {
int xCheck = Mathf.FloorToInt(pos.x);
int zCheck = Mathf.FloorToInt(pos.z);
x = xCheck / VoxelData.ChunkWidth;
z = zCheck / VoxelData.ChunkWidth;
}
public bool Equals (ChunkCoord other) {
if (other == null)
return false;
else if (other.x == x && other.z == z)
return true;
else
return false;
}
}
这些是主要的 2 个脚本,如果您需要更多,请告诉我。
哦,所以我取消了评论,我让它工作了,谢谢。