- Home /
Voxel Brush throwing Null Reference
Hello all,
I'm currently in the midst of working on a voxel engine, using C#.
Now, I have managed to follow along with some tutorials, and I have a "world" that generates chunks, and these chunks are filled with blocktype. I am able to build/destroy on any generated chunk i wish.
NOTE- I have not implemented any kind of Perlin noise, or "real" terrain generation. It is currently just a flat world.
Now up to this point everything works great. The world is "randomly" generated as you "explore". But when I tried to implement a brush, it broke.
First off when using a brush, the "world" technically still generates chunks, the first problem is that only a handful of the chunks are filled with blocks. The remaining chunks are only empty prefabs. Then I get an error, Null Reference.
The Second problem is that when using the brush, the brushes don't generate at all. As mentioned the chunks still generate, most unfilled, however not a single brush generates.
and heres the code , with error- Any help is appreciated.
NullReferenceException Chunk.Regenerate () (at Assets/MyAssets/Scripts/Chunk.cs:179) Chunk.SetBrick (Int32 x, Int32 y, Int32 z, Byte block) (at Assets/MyAssets/Scripts/Chunk.cs:215) Chunk.ApplyBrush (LandBrush brush) (at Assets/MyAssets/Scripts/Chunk.cs:72) Chunk.Start () (at Assets/MyAssets/Scripts/Chunk.cs:32)
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
[RequireComponent (typeof(MeshFilter))]
[RequireComponent (typeof(MeshCollider))]
public class Chunk : MonoBehaviour {
public int width = 20;
public byte[,,] map;
protected Mesh mesh;
protected List<Vector3> verts = new List<Vector3>();
protected List<int> tris = new List<int>();
protected List<Vector2> uv = new List<Vector2>();
public struct LandBrush {
public Vector3 pos;
public float size;
public byte block;
}
protected MeshCollider meshCollider;
// Use this for initialization
void Start () {
map = new byte[width, width, width];
LandBrush brush = CreateBrush();
ApplyBrush(brush);
for (int x = 0; x < width; x++)
{
for (int z = 0; z < width; z++)
{
map[x, 0, z] = 1;
}
}
meshCollider = GetComponent<MeshCollider>();
mesh = new Mesh();
GetComponent<MeshFilter>().mesh = mesh;
Regenerate();
}
// Update is called once per frame
void Update () {
}
public void ApplyBrush(LandBrush brush) {
float minY = brush.pos.y - brush.size;
float maxY = brush.pos.y + brush.size;
float minZ = brush.pos.z - brush.size;
float maxZ = brush.pos.z + brush.size;
for (float x = brush.pos.x - brush.size; x <= brush.pos.x + brush.size; x++)
{
for (float y = minY; y <= maxY; y++)
{
for (float z = minZ; z<= maxZ; z++)
{
Vector3 p = new Vector3(x,y,z);
p -= brush.pos;
if (p.magnitude >= brush.size) continue;
SetBrick( Mathf.FloorToInt(x), Mathf.FloorToInt(y), Mathf.FloorToInt(z), brush.block);
}
}
}
}
public void DrawBrick(int x, int y, int z, byte block)
{
Vector3 start = new Vector3(x,y,z);
Vector3 offset1, offset2;
if (IsTransparent(x,y-1,z))
{
offset1 = Vector3.left;
offset2 = Vector3.back;
DrawFace(start + Vector3.right,offset1,offset2,block);
}
if (IsTransparent(x,y+1,z))
{
offset1 = Vector3.right;
offset2 = Vector3.back;
DrawFace(start + Vector3.up,offset1,offset2,block);
}
if (IsTransparent(x-1,y,z))
{
offset1 = Vector3.up;
offset2 = Vector3.back;
DrawFace(start,offset1,offset2,block);
}
if (IsTransparent(x+1,y,z))
{
offset1 = Vector3.down;
offset2 = Vector3.back;
DrawFace(start + Vector3.right + Vector3.up,offset1,offset2,block);
}
if (IsTransparent(x,y,z-1))
{
offset1 = Vector3.left;
offset2 = Vector3.up;
DrawFace(start + Vector3.right + Vector3.back,offset1,offset2,block);
}
if (IsTransparent(x,y,z+1))
{
offset1 = Vector3.right;
offset2 = Vector3.up;
DrawFace(start,offset1,offset2,block);
}
}
public void DrawFace(Vector3 start, Vector3 offset1, Vector3 offset2, byte block)
{
int index = verts.Count;
verts.Add (start);
verts.Add (start + offset1);
verts.Add (start + offset2);
verts.Add (start + offset1 + offset2);
Vector2 uvBase;
switch (block)
{
default:
uvBase = new Vector2(0.25f, 0.25f);
break;
case 2:
uvBase = new Vector2(0.75f, 0.75f);
break;
case 3:
uvBase = new Vector2(0.25f, 0.75f);
break;
case 4:
uvBase = new Vector2(0.75f, 1f);
break;
}
if ((offset1 == Vector3.right) && (offset2 == Vector3.back))
{
uv.Add (uvBase);
uv.Add (uvBase + new Vector2(0.125f, 0));
uv.Add (uvBase + new Vector2(0, 0.125f));
uv.Add (uvBase + new Vector2(0.125f, 0.125f));
}
else
{
uv.Add (uvBase);
uv.Add (uvBase + new Vector2(-0.125f, 0));
uv.Add (uvBase + new Vector2(0, 0.125f));
uv.Add (uvBase + new Vector2(-0.125f, 0.125f));
}
tris.Add(index + 0);
tris.Add(index + 1);
tris.Add(index + 2);
tris.Add(index + 3);
tris.Add(index + 2);
tris.Add(index + 1);
}
public bool IsTransparent(int x, int y, int z)
{
if ((x<0) || (y<0) || (z<0) || (x>= width) || (y>=width) || (z>=width)) return true;
return map[x,y,z] == 0;
}
public void Regenerate() {
verts.Clear();
tris.Clear();
uv.Clear();
mesh.triangles = tris.ToArray();
for (int x = 0; x < width; x++)
{
for (int y = 0; y < width; y++)
{
for (int z = 0; z < width; z++)
{
byte block = map[x,y,z];
if (block == 0) continue;
DrawBrick(x,y,z,block);
}
}
}
mesh.vertices = verts.ToArray();
mesh.triangles = tris.ToArray();
mesh.uv = uv.ToArray();
mesh.RecalculateNormals();
meshCollider.sharedMesh = null;
meshCollider.sharedMesh = mesh;
}
public void SetBrick(int x, int y, int z, byte block){
x -= Mathf.RoundToInt(transform.position.x);
y -= Mathf.RoundToInt(transform.position.y);
z -= Mathf.RoundToInt(transform.position.z);
if ((x<0) || (y<0) || (z<0) || (x>= width) || (y>=width) || (z>=width)) return;
if (map[x,y,z] !=block)
{
map[x,y,z] = block;
Regenerate();
}
}
public LandBrush CreateBrush(){
LandBrush brush = new LandBrush();
brush.pos = new Vector3(Random.value * width + transform.position.x, 0, Random.value * width + transform.position.z);
brush.size = Random.value * width / 2;
brush.block = 1;
return brush;
}
}
Answer by Peter G · Aug 06, 2013 at 11:15 PM
First, if you don't know what a NullReferenceException is, read this.
Here's what I think is happen. Now given you have a lot of if
statement wrappers and I didn't really follow them to see if they'd get called, but this is a possibility.
It would appear mesh
is null. Now at first glance it doesn't appear to be used before you call Regenerate()
, but following a chain of methods, you get this:
ApplyBrush() -> calls SetBrick() -> calls Regenerate()
Again I didn't check to make sure all of the if
statements would be true, but that could be it. If mesh
isn't null
, then your list is null.
I think the easiest way to fix that is just to initialize the mesh
above ApplyBrush()
.
Your answer
Follow this Question
Related Questions
Distribute terrain in zones 3 Answers
15625 blocks are making my game lag? Increase performance? 3 Answers
Multiple Cars not working 1 Answer
SetAlphaMaps Opacity Gradient 0 Answers
Ingame Texture Switcher 1 Answer