- Home /
The question is answered, right answer was accepted
Why isn't my mesh updating properly?
I'm designing a game that uses "chunks" to store "blocks" similar to Minecraft.
When a block is placed down, it gets its vertices and triangles assigned, as well as the uv vertices (though I don't think I'm doing those properly but that's for another question). It also is assigned as a child to whichever chunk it's in.
There is a Boolean array in the chunk that keeps track of where each block is placed based on relative position. Ex: blockPos[transform.localPosition.x, ---.y, ---.z]
When a block is placed it is also supposed to call a function in the parent which then calls a function in each child to re-evaluate the mesh.
My problem is that the meshes do not update when the ChunkUpdate() function is called. They work properly only on initial placement.
Below is the script attached to the Chunk Object. Below that is the script attached to the Block Object.
using UnityEngine;
using System.Collections;
public class ChunkScript : MonoBehaviour {
public bool[,,] blockPos = new bool[10,300,10];
public void ChunkUpdate() {
foreach (Transform child in transform) {
BlockScript childScript = child.GetComponent<BlockScript>();
childScript.CreateFaces();
}
}
}
Below: Script attached to block object
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class BlockScript : MonoBehaviour {
//Code for rendering mesh
Vector3[] vertices = new Vector3[8];
List<int> trianglesOne = new List<int>();
Vector2[] uvs = new Vector2[8];
Mesh mesh;
public Material wood;
//Vector3 pos;
//End of code for rendering mesh
ChunkScript chunkScript;
public GameObject CoordChecker;
int xPos;
int yPos;
int zPos;
void AddComponents() {
gameObject.AddComponent<MeshFilter>();
gameObject.AddComponent<MeshRenderer>();
gameObject.AddComponent<BoxCollider>();
BoxCollider box = gameObject.GetComponent<BoxCollider>();
box.size = new Vector3(.99f, .99f, .99f);
mesh = GetComponent<MeshFilter>().mesh;
if (GlobalVariableHolder.wood) gameObject.GetComponent<MeshRenderer>().material = wood;
gameObject.tag = "Block";
}
void Start() {
AddComponents();
}
public void printName() {
print(name);
}
void OnTriggerEnter(Collider other) {
if (other.tag == "Chunk") {
transform.parent = other.transform;
xPos = (int)transform.localPosition.x;
yPos = (int)transform.localPosition.y;
zPos = (int)transform.localPosition.z;
transform.name = (xPos + " " + yPos + " " + zPos);
chunkScript = transform.parent.GetComponent<ChunkScript>();
chunkScript.blockPos[xPos, yPos, zPos] = true;
CreateFaces();
chunkScript.ChunkUpdate();
}
}
void OnDestroy() {
chunkScript.blockPos[xPos, yPos, zPos] = false;
chunkScript.ChunkUpdate();
}
//Checks if each direction has a block and calls the respective function to draw the face
public void CreateFaces() {
mesh.Clear();
Vertices();
if (zPos < 9) {
if (chunkScript.blockPos[xPos, yPos, zPos + 1] == false) FaceNorth();
} else if(zPos == 9) {
GameObject checker = Instantiate(CoordChecker, new Vector3(xPos, yPos, zPos + 1), Quaternion.identity) as GameObject;
CoordCheckerScript checkerScript = checker.GetComponent<CoordCheckerScript>();
if (checkerScript.containsBlock == false) { FaceNorth(); }
Destroy(checker.gameObject);
}
if (xPos < 9) {
if (!chunkScript.blockPos[xPos + 1, yPos, zPos]) FaceEast();
} else if(xPos == 9) {
GameObject checker = Instantiate(CoordChecker, new Vector3(xPos + 1, yPos, zPos), Quaternion.identity) as GameObject;
CoordCheckerScript checkerScript = checker.GetComponent<CoordCheckerScript>();
if (checkerScript.containsBlock == false) { FaceEast(); }
Destroy(checker.gameObject);
}
if (zPos > 0) {
if (!chunkScript.blockPos[xPos, yPos, zPos - 1]) FaceSouth();
} else if(zPos == 0) {
GameObject checker = Instantiate(CoordChecker, new Vector3(xPos, yPos, zPos - 1), Quaternion.identity) as GameObject;
CoordCheckerScript checkerScript = checker.GetComponent<CoordCheckerScript>();
if (checkerScript.containsBlock == false) { FaceSouth(); }
Destroy(checker.gameObject);
}
if (xPos > 0) {
if (!chunkScript.blockPos[xPos - 1, yPos, zPos]) FaceWest();
} else if (xPos == 0) {
GameObject checker = Instantiate(CoordChecker, new Vector3(xPos - 1, yPos, zPos), Quaternion.identity) as GameObject;
CoordCheckerScript checkerScript = checker.GetComponent<CoordCheckerScript>();
if (checkerScript.containsBlock == false) { FaceWest(); }
Destroy(checker.gameObject);
}
if (yPos < 299) {
if (!chunkScript.blockPos[xPos, yPos + 1, zPos]) FaceTop();
}
if (yPos > 0) {
if (!chunkScript.blockPos[xPos, yPos - 1, zPos]) FaceBottom();
}
int[] triangles = trianglesOne.ToArray();
mesh.triangles = triangles;
}
//Code to set mesh triangles
void Vertices() {
vertices[0] = new Vector3(-0.5f, -0.5f, -0.5f);
vertices[1] = new Vector3(-0.5f, 0.5f, -0.5f);
vertices[2] = new Vector3(0.5f, 0.5f, -0.5f);
vertices[3] = new Vector3(0.5f, -0.5f, -0.5f);
vertices[4] = new Vector3(-0.5f, -0.5f, 0.5f);
vertices[5] = new Vector3(-0.5f, 0.5f, 0.5f);
vertices[6] = new Vector3(0.5f, 0.5f, 0.5f);
vertices[7] = new Vector3(0.5f, -0.5f, 0.5f);
mesh.vertices = vertices;
for(int i = 0; i < vertices.Length; i++) {
uvs[i] = new Vector2(vertices[i].x, vertices[i].z);
}
mesh.uv = uvs;
}
void FaceNorth() {
trianglesOne.Add(7);
trianglesOne.Add(6);
trianglesOne.Add(5);
trianglesOne.Add(7);
trianglesOne.Add(5);
trianglesOne.Add(4);
}
void FaceEast() {
trianglesOne.Add(3);
trianglesOne.Add(2);
trianglesOne.Add(6);
trianglesOne.Add(3);
trianglesOne.Add(6);
trianglesOne.Add(7);
}
void FaceSouth() {
trianglesOne.Add(0);
trianglesOne.Add(1);
trianglesOne.Add(2);
trianglesOne.Add(0);
trianglesOne.Add(2);
trianglesOne.Add(3);
}
void FaceWest() {
trianglesOne.Add(4);
trianglesOne.Add(5);
trianglesOne.Add(1);
trianglesOne.Add(4);
trianglesOne.Add(1);
trianglesOne.Add(0);
}
void FaceTop() {
trianglesOne.Add(1);
trianglesOne.Add(5);
trianglesOne.Add(6);
trianglesOne.Add(1);
trianglesOne.Add(6);
trianglesOne.Add(2);
}
void FaceBottom() {
trianglesOne.Add(4);
trianglesOne.Add(0);
trianglesOne.Add(3);
trianglesOne.Add(4);
trianglesOne.Add(3);
trianglesOne.Add(7);
}
The If statements in BlockScript check for adjacent blocks inside the chunk as well as outside the chunk.
Any help is appreciated!
EDIT I removed the mesh.clear in the ChunkScript because I clear the mesh in BlockScript.CreateFaces();
EDIT TWO I also commented out "CreateFaces();" in BlockScript.OnTriggerEnter() because of the redundancy in calling CreateFaces() and then calling ChunkScript.ChunkUpdate() which then calls CreateFaces();
EDIT THREE I added a call to ChunkScript.ChunkUpdate() in BlockScript.OnDestroy(); After testing with that line of code, I notice something:
Example One: Place A down, then place B down to east of A: A has all sides rendered, B has all sides rendered but west side;
Example Two: Place A down, then place B down to east of A, then delete A: A has all sides rendered originally, B has all sides but west rendered originally, Upon deletion of A, B has all sides rendered;
Answer by Browninge · Jun 03, 2016 at 12:18 PM
For anyone who is having similar problems to what I am(was, rather) having, I will explain the source of my problem and how I solved it.
Since I only wanted to generate certain faces, I would push the triangle vertices to a list under each Face[Direction]() function. I would then generate an array from that list. In order to "undraw" a face that has already been drawn, you have to clear the list that you stored the triangles in originally (for my particular situation, I just had to add in "trianglesOne.Clear();" at the start of my CreateFaces() function.
Answer by Glurth · May 28, 2016 at 03:11 PM
It looks like you clear the array in the mesh filter, and generate a new mesh, stored in the BlockScript class. However, I do NOT see where you re-assign that computed mesh BACK to the mesh filter. Until you do so, (assuming the object also has a MeshRenderer), you will not be able to see it, since nothing is assigned to render it.
(p.s. for your initial loop, rather than transform.children, take a look at GetComponentsInChildren(). This can get all the MeshFilters of children, into an array for looping. Eliminates need to check for components against null, something you are not doing in that loop, but probably should..)
I'm not picking up what you're putting down. From my understanding, BlockScript.CreateFaces() handles everything from declaring the vertices, deter$$anonymous$$ing what sides to generate, declaring the triangles, and then assigning those triangles to the mesh. What exactly happens upon initial placement that doesn't happen upon subsequent calls to BlockScript.CreateFaces()?
Also, I edited a few lines of code and I state exactly what I changed at the bottom of my original post. Everything still works exactly as it did before though, $$anonymous$$us a couple redundant calls.
Also again, I played around changing Line 81 from ...FaceNorth(); to ...print(name + " North is free"); to test that it correctly rechecks the Boolean. I have confirmed that it does work properly on initial placement and subsequent calls to BlockScript.CreateFaces().
I ask that you check the third edit that I made (I state what I changed at the end of the original post). The current situation leads me to suspect that somehow a block doesn't register subsequently placed adjacent blocks, even though it registers the deletion of adjacent blocks that were placed prior. However, my original comment to your reply suggests that blocks do register subsequently placed adjacent blocks.
I will also add in what you said about GetComponentsInChildren(), but after I figure the current dilemma out.
Hmm, I may have been incorrect in my suggestion. I was talking about this line:
mesh = GetComponent<$$anonymous$$eshFilter>().mesh;
Which is how you get the $$anonymous$$esh you want to work with. I guess if you adjust THIS mesh, it wont need to be reassignned. This threw me off, because normally, I would create a NEW mesh, using new $$anonymous$$esh()
, and then after building it, assign THAT to the $$anonymous$$eshFilter component.
GetComponent<$$anonymous$$eshFilter>().mesh= newlyCreated$$anonymous$$esh;
Perhaps this is related to the issue in a different way I expected though. You mentioned that edit three caused changes in existsing meshes. Perhaps the mesh you are getting and modifying is actually in use by the unexpectedly modified object? If so, you probably DO want to create a new mesh for each block, as mentioned above..
I understand exactly what you're saying and will play around with that and get back to you.
WAIT A $$anonymous$$INUTE I THIN$$anonymous$$ I $$anonymous$$NOW WHAT IS WRONG. I am pretty sure the issue comes from me not clearing the triangle list/array. I think that's my problem.