- Home /
Maths issue + Null reference error?
So , for the last few days i have been confused with this and i really cant see where the issue is, so i have my voxel game, i am implementing chunk loading into the game, i do this by getting the edge chunks at opposite direction to the players direction, clear their chunk data, move the chunk objects in front of them and recalculate the chunk data giving the illusion that they are exploring new chunks, i am doing this for optimization purposes, the only thing is that i am having a few issues, mainly with random math issues, at certain points, a certain chunk , specifically at a Z of -8 will randomly offset itself twice as far for no reason, i also noticed that , before i was casting the positions to integers, the values would equal a random float value, the error i get is :
NullReferenceException
UnityEngine.GameObject.GetComponent[ChunkRenderer] () (at C:/BuildAgent/work/cac08d8a5e25d4cb/Runtime/ExportGenerated/Editor/UnityEngineGameObject.cs:27)
ChunkManager.loadChunkLayer (Int32 direction) (at Assets/classes/base/World/ChunkManager.cs:1023)
ChunkManager.Update () (at Assets/classes/base/World/ChunkManager.cs:74)
which is basically the code :
chunkQueueRender[index] = GameObject.Find((startPosition.x + i) + "," + (startPosition.y + j) + "," + ((startPosition.z + zO) - 1)).GetComponent<ChunkRenderer>();
Here is the whole function:
public void loadChunkLayer(int direction)
{
ChunkRenderer[] chunkQueueUnload; // the chunks we will be unloading and moving
ChunkRenderer[] chunkQueueRender; // the chunks we will be redrawing which are adjacent to the new locations
Vector3 startPosition; // the position at which we start searching the chunks
int index = 0; // a helper variable to keep track of our index within loops
int X, Y, Z; // holder variables for Baseclass chunk dimension variables for ease
X = BaseClass.WORLD_CHUNK_DIMENSION_X;
Y = BaseClass.WORLD_CHUNK_DIMENSION_Y;
Z = BaseClass.WORLD_CHUNK_DIMENSION_Z;
int x, y, z; // holder variables for Baseclass chunk loaded range variables for ease
x = BaseClass.WORLDCHUNK_LOADED_LIMIT_X;
y = BaseClass.WORLDCHUNK_LOADED_LIMIT_Y;
z = BaseClass.WORLDCHUNK_LOADED_LIMIT_Z;
int XO, YO, ZO; // holder variables for Baseclass chunk dimension values as offsets
XO = X - 1;
YO = Y - 1;
ZO = Z - 1;
int xO, yO, zO; // holder variables for Baseclass chunk loaded range values as offsets
xO = x - 1;
yO = y - 1;
zO = z - 1;
switch (direction) // execute the correct code dependant on direction we feed the function
{
case 0:
index = 0;
// Called when we are loading chunks " Upwards "
chunkQueueUnload = new ChunkRenderer[x * z]; // initialize the unload queue, it will be the size of a single slice of the loaded world
chunkQueueRender = new ChunkRenderer[x * z]; // initialize the re-render queue, it will be the size of a single slice of the loaded world
startPosition = new Vector3((cornerLoadedChunk.x), (cornerLoadedChunk.y), (cornerLoadedChunk.z)); // set the starting point
for (int i = 0; i < x; i++) // search for the chunks in the world
{
for (int j = 0; j < z; j++)
{
chunkQueueUnload[index] = GameObject.Find((startPosition.x + i) + "," + (startPosition.y) + "," + (startPosition.z + j)).GetComponent<ChunkRenderer>();
chunkQueueRender[index] = GameObject.Find((startPosition.x + i) + "," + ((startPosition.y + yO) - 1) + "," + (startPosition.z + j)).GetComponent<ChunkRenderer>();
index++; // this value is very important because it keeps track of the array index we are currently at
}
}
for (int i = 0; i < chunkQueueUnload.Length; i++) // loop through and get each chunk what we want to unload
{
chunkQueueUnload[i].onChunkUnloaded(); // call any last code before we unload the chunk
if (!chunkMap.hasChunk(chunkQueueUnload[i].pos)) // does our chunk already exist in the world or is it new?
{
chunkQueueUnload[i].transform.position += new Vector3(0, y * Y, 0); // apply the offset to the chunks position
Vector3 tempPos = chunkQueueUnload[i].transform.position; // store our position for ease of use
chunkQueueUnload[i].gameObject.name = ((int)tempPos.x / X) + "," + ((int)tempPos.y / Y) + "," + ((int)tempPos.z / Z); // rename this chunk to the new name matching its position
chunkQueueUnload[i].initChunk(X, Y, Z, new Vector3(tempPos.x / X, tempPos.y / Y, tempPos.z / Z)); // re-initialize the chunk with new data
}
else // our chunk is already existant in the world, this means that we need to load the chunk data
{
// LOAD CHUNK WORLD DATA HERE
}
}
cornerLoadedChunk += new Vector3(0, 1, 0); // add an offset to our world corner position
for (int i = 0; i < chunkQueueUnload.Length; i++) // loop through the chunks which need updating to redraw the meshes
{
chunkQueueUnload[i].updateChunkMesh();
}
for (int i = 0; i < chunkQueueRender.Length; i++) // loop through the chunks which need re-rendering to redraw the meshes
{
chunkQueueRender[i].updateChunkMesh();
}
break;
case 1:
index = 0;
// Called when we are loading chunks " Downwards "
chunkQueueUnload = new ChunkRenderer[x * z]; // initialize the unload queue, it will be the size of a single slice of the loaded world
chunkQueueRender = new ChunkRenderer[x * z]; // initialize the re-render queue, it will be the size of a single slice of the loaded world
startPosition = new Vector3((cornerLoadedChunk.x), (cornerLoadedChunk.y+ yO), (cornerLoadedChunk.z)); // set the starting point
for (int i = 0; i < x; i++) // search for the chunks in the world
{
for (int j = 0; j < z; j++)
{
chunkQueueUnload[index] = GameObject.Find((startPosition.x + i) + "," + (startPosition.y) + "," + (startPosition.z + j)).GetComponent<ChunkRenderer>();
chunkQueueRender[index] = GameObject.Find((startPosition.x + i) + "," + ((startPosition.y - yO) + 1) + "," + (startPosition.z + j)).GetComponent<ChunkRenderer>();
index++; // this value is very important because it keeps track of the array index we are currently at
}
}
for (int i = 0; i < chunkQueueUnload.Length; i++) // loop through and get each chunk what we want to unload
{
chunkQueueUnload[i].onChunkUnloaded(); // call any last code before we unload the chunk
if (!chunkMap.hasChunk(chunkQueueUnload[i].pos)) // does our chunk already exist in the world or is it new?
{
chunkQueueUnload[i].transform.position -= new Vector3(0, y * Y, 0); // apply the offset to the chunks position
Vector3 tempPos = chunkQueueUnload[i].transform.position; // store our position for ease of use
chunkQueueUnload[i].gameObject.name = ((int)tempPos.x / X) + "," + ((int)tempPos.y / Y) + "," + ((int)tempPos.z / Z); // rename this chunk to the new name matching its position
chunkQueueUnload[i].initChunk(X, Y, Z, new Vector3(tempPos.x / X, tempPos.y / Y, tempPos.z / Z)); // re-initialize the chunk with new data
}
else // our chunk is already existant in the world, this means that we need to load the chunk data
{
// LOAD CHUNK WORLD DATA HERE
}
}
cornerLoadedChunk += new Vector3(0, -1, 0); // add an offset to our world corner position
for (int i = 0; i < chunkQueueUnload.Length; i++) // loop through the chunks which need updating to redraw the meshes
{
chunkQueueUnload[i].updateChunkMesh();
}
for (int i = 0; i < chunkQueueRender.Length; i++) // loop through the chunks which need re-rendering to redraw the meshes
{
chunkQueueRender[i].updateChunkMesh();
}
break;
case 2:
index = 0;
// Called when we are loading chunks " left "
chunkQueueUnload = new ChunkRenderer[x * y]; // initialize the unload queue, it will be the size of a single slice of the loaded world
chunkQueueRender = new ChunkRenderer[x * y]; // initialize the re-render queue, it will be the size of a single slice of the loaded world
startPosition = new Vector3((cornerLoadedChunk.x), (cornerLoadedChunk.y), (cornerLoadedChunk.z)); // set the starting point
for (int i = 0; i < x; i++) // search for the chunks in the world
{
for (int j = 0; j < y; j++)
{
chunkQueueUnload[index] = GameObject.Find((startPosition.x) + "," + (startPosition.y + j) + "," + (startPosition.z + i)).GetComponent<ChunkRenderer>();
chunkQueueRender[index] = GameObject.Find(((startPosition.x + xO) - 1) + "," + (startPosition.y + j) + "," + (startPosition.z + i)).GetComponent<ChunkRenderer>();
index++; // this value is very important because it keeps track of the array index we are currently at
}
}
for (int i = 0; i < chunkQueueUnload.Length; i++) // loop through and get each chunk what we want to unload
{
chunkQueueUnload[i].onChunkUnloaded(); // call any last code before we unload the chunk
if (!chunkMap.hasChunk(chunkQueueUnload[i].pos)) // does our chunk already exist in the world or is it new?
{
chunkQueueUnload[i].transform.position += new Vector3(x * X, 0, 0); // apply the offset to the chunks position
Vector3 tempPos = chunkQueueUnload[i].transform.position; // store our position for ease of use
chunkQueueUnload[i].gameObject.name = ((int)tempPos.x / X) + "," + ((int)tempPos.y / Y) + "," + ((int)tempPos.z / Z); // rename this chunk to the new name matching its position
chunkQueueUnload[i].initChunk(X, Y, Z, new Vector3(tempPos.x / X, tempPos.y / Y, tempPos.z / Z)); // re-initialize the chunk with new data
}
else // our chunk is already existant in the world, this means that we need to load the chunk data
{
// LOAD CHUNK WORLD DATA HERE
}
}
cornerLoadedChunk += new Vector3(1, 0, 0); // add an offset to our world corner position
for (int i = 0; i < chunkQueueUnload.Length; i++) // loop through the chunks which need updating to redraw the meshes
{
chunkQueueUnload[i].updateChunkMesh();
}
for (int i = 0; i < chunkQueueRender.Length; i++) // loop through the chunks which need re-rendering to redraw the meshes
{
chunkQueueRender[i].updateChunkMesh();
}
break;
case 3:
index = 0;
// Called when we are loading chunks " Right "
chunkQueueUnload = new ChunkRenderer[x * y]; // initialize the unload queue, it will be the size of a single slice of the loaded world
chunkQueueRender = new ChunkRenderer[x * y]; // initialize the re-render queue, it will be the size of a single slice of the loaded world
startPosition = new Vector3((cornerLoadedChunk.x + xO), (cornerLoadedChunk.y), (cornerLoadedChunk.z)); // set the starting point
for (int i = 0; i < x; i++) // search for the chunks in the world
{
for (int j = 0; j < y; j++)
{
chunkQueueUnload[index] = GameObject.Find((startPosition.x) + "," + (startPosition.y + j) + "," + (startPosition.z + i)).GetComponent<ChunkRenderer>();
chunkQueueRender[index] = GameObject.Find((startPosition.x- 1) + "," + (startPosition.y + j) + "," + (startPosition.z + i)).GetComponent<ChunkRenderer>();
index++; // this value is very important because it keeps track of the array index we are currently at
}
}
for (int i = 0; i < chunkQueueUnload.Length; i++) // loop through and get each chunk what we want to unload
{
chunkQueueUnload[i].onChunkUnloaded(); // call any last code before we unload the chunk
if (!chunkMap.hasChunk(chunkQueueUnload[i].pos)) // does our chunk already exist in the world or is it new?
{
chunkQueueUnload[i].transform.position -= new Vector3(x * X, 0, 0); // apply the offset to the chunks position
Vector3 tempPos = chunkQueueUnload[i].transform.position; // store our position for ease of use
chunkQueueUnload[i].gameObject.name = ((int)tempPos.x / X) + "," + ((int)tempPos.y / Y) + "," + ((int)tempPos.z / Z); // rename this chunk to the new name matching its position
chunkQueueUnload[i].initChunk(X, Y, Z, new Vector3(tempPos.x / X, tempPos.y / Y, tempPos.z / Z)); // re-initialize the chunk with new data
}
else // our chunk is already existant in the world, this means that we need to load the chunk data
{
// LOAD CHUNK WORLD DATA HERE
}
}
cornerLoadedChunk += new Vector3(-1, 0, 0); // add an offset to our world corner position
for (int i = 0; i < chunkQueueUnload.Length; i++) // loop through the chunks which need updating to redraw the meshes
{
chunkQueueUnload[i].updateChunkMesh();
}
for (int i = 0; i < chunkQueueRender.Length; i++) // loop through the chunks which need re-rendering to redraw the meshes
{
chunkQueueRender[i].updateChunkMesh();
}
break;
case 4:
index = 0;
// Called when we are loading chunks " forward "
chunkQueueUnload = new ChunkRenderer[z * y]; // initialize the unload queue, it will be the size of a single slice of the loaded world
chunkQueueRender = new ChunkRenderer[z * y]; // initialize the re-render queue, it will be the size of a single slice of the loaded world
startPosition = new Vector3((cornerLoadedChunk.x), (cornerLoadedChunk.y), (cornerLoadedChunk.z)); // set the starting point
for (int i = 0; i < z; i++) // search for the chunks in the world
{
for (int j = 0; j < y; j++)
{
chunkQueueUnload[index] = GameObject.Find((startPosition.x + i) + "," + (startPosition.y + j) + "," + (startPosition.z)).GetComponent<ChunkRenderer>();
chunkQueueRender[index] = GameObject.Find((startPosition.x + i) + "," + (startPosition.y + j) + "," + ((startPosition.z + zO) - 1)).GetComponent<ChunkRenderer>();
index++; // this value is very important because it keeps track of the array index we are currently at
}
}
for (int i = 0; i < chunkQueueUnload.Length; i++) // loop through and get each chunk what we want to unload
{
chunkQueueUnload[i].onChunkUnloaded(); // call any last code before we unload the chunk
if (!chunkMap.hasChunk(chunkQueueUnload[i].pos)) // does our chunk already exist in the world or is it new?
{
chunkQueueUnload[i].transform.position += new Vector3(0, 0, z * Z); // apply the offset to the chunks position
Vector3 tempPos = chunkQueueUnload[i].transform.position; // store our position for ease of use
chunkQueueUnload[i].gameObject.name = ((int)tempPos.x / X) + "," + ((int)tempPos.y / Y) + "," + ((int)tempPos.z / Z); // rename this chunk to the new name matching its position
chunkQueueUnload[i].initChunk(X, Y, Z, new Vector3(tempPos.x / X, tempPos.y / Y, tempPos.z / Z)); // re-initialize the chunk with new data
}
else // our chunk is already existant in the world, this means that we need to load the chunk data
{
// LOAD CHUNK WORLD DATA HERE
}
}
cornerLoadedChunk += new Vector3(0, 0, 1); // add an offset to our world corner position
for (int i = 0; i < chunkQueueUnload.Length; i++) // loop through the chunks which need updating to redraw the meshes
{
chunkQueueUnload[i].updateChunkMesh();
}
for (int i = 0; i < chunkQueueRender.Length; i++) // loop through the chunks which need re-rendering to redraw the meshes
{
chunkQueueRender[i].updateChunkMesh();
}
break;
case 5:
index = 0;
// Called when we are loading chunks " backwards "
chunkQueueUnload = new ChunkRenderer[z * y]; // initialize the unload queue, it will be the size of a single slice of the loaded world
chunkQueueRender = new ChunkRenderer[z * y]; // initialize the re-render queue, it will be the size of a single slice of the loaded world
startPosition = new Vector3(((int)cornerLoadedChunk.x), ((int)cornerLoadedChunk.y), ((int)cornerLoadedChunk.z + zO)); // set the starting point
for (int i = 0; i < z; i++) // search for the chunks in the world
{
for (int j = 0; j < y; j++)
{
chunkQueueUnload[index] = GameObject.Find((startPosition.x + i) + "," + (startPosition.y + j) + "," + (startPosition.z)).GetComponent<ChunkRenderer>();
try
{
chunkQueueRender[index] = GameObject.Find((startPosition.x + i) + "," + (startPosition.y + j) + "," + (startPosition.z - 1)).GetComponent<ChunkRenderer>();
}
catch
{
print("Exception getting chunk position, tried to get a chunk at " + new Vector3(startPosition.x + i, startPosition.y + j, startPosition.z - 1));
}
index++; // this value is very important because it keeps track of the array index we are currently at
}
}
for (int i = 0; i < chunkQueueUnload.Length; i++) // loop through and get each chunk what we want to unload
{
chunkQueueUnload[i].onChunkUnloaded(); // call any last code before we unload the chunk
if (!chunkMap.hasChunk(chunkQueueUnload[i].pos)) // does our chunk already exist in the world or is it new?
{
chunkQueueUnload[i].transform.position -= new Vector3(0, 0, (z * Z)); // apply the offset to the chunks position
Vector3 tempPos = chunkQueueUnload[i].transform.position; // store our position for ease of use
chunkQueueUnload[i].gameObject.name = ((int)tempPos.x / X) + "," + ((int)tempPos.y / Y) + "," + ((int)tempPos.z / Z); // rename this chunk to the new name matching its position
chunkQueueUnload[i].initChunk(X, Y, Z, new Vector3(tempPos.x / X, tempPos.y / Y, tempPos.z / Z)); // re-initialize the chunk with new data
}
else // our chunk is already existant in the world, this means that we need to load the chunk data
{
// LOAD CHUNK WORLD DATA HERE
}
}
cornerLoadedChunk += new Vector3(0, 0, -1); // add an offset to our world corner position
for (int i = 0; i < chunkQueueUnload.Length; i++) // loop through the chunks which need updating to redraw the meshes
{
chunkQueueUnload[i].updateChunkMesh();
}
for (int i = 0; i < chunkQueueRender.Length; i++) // loop through the chunks which need re-rendering to redraw the meshes
{
chunkQueueRender[i].updateChunkMesh();
}
break;
}
}
Thanks, and please, tell me if you notice anything at all :)
From the error you get, I take it one of your objects you're trying to find is not found (na$$anonymous$$g problem?)
You should add a debug of your object's name before
chunkQueueRender[index] = GameObject.Find((startPosition.x + i) + "," + (startPosition.y + j) + "," + ((startPosition.z + zO) - 1)).GetComponent<ChunkRenderer>();
So you know what object you're trying to find (and cannot) before this exception is thrown.
for some reason, it ends up with a really weird value , why does it end up putting the Z at " 3.814697e-06 " ?
hmm, you should not be working with floats for na$$anonymous$$g. They are inherently inaccurate.
I think you should learn to read error messages in your stack trace.
They tell you exactly what line the error is on, and which lines you were on to get to that one.
Giving us all the code is sometimes less useful than just giving use the line its being thrown on and any necessary context.
Answer by ZenithCode · Aug 12, 2013 at 09:03 PM
Be careful when doing something like: chunkQueueUnload[index] = GameObject.Find((startPosition.x + i) + "," + (startPosition.y + j) + "," + (startPosition.z)).GetComponent();
Split this into:
string objName = (startPosition.x + i) + "," + (startPosition.y + j) + "," + (startPosition.z);
Debug.Log("Object name is: " + objName);
GameObject go = GameObject.Find(objName) as GameObject;
Debug.Log("Found GameObject: " + go);
ChunkRenderer ck = go.GetComponent<ChunkRenderer>();
Debug.Log("ChunkRendere: " + ck);
chunkQueueUnload[index] = ck;
Debug.Log("Success" );
`
One of these will give you more information on what is actually null.
ill be sure to add that, i was thinking of performance when i made it but yeah, do you see any way that the values could be problematic? especially when negative? it only seems to happen when negative, do you see any flaws with the maths anywhere?
The obvious flaw is in the na$$anonymous$$g convention. Casting a float to int will also loose accuracy.
Did you fix the null ref at least?
Casting does not fix this issue, i still have a null reference for some reason and cant see the cause of it. im trying new ways, going to try debug everything about it first hand