- Home /
How do I add a MeshCollider to my randomly generated island through Code
Hi, so im working on a procedural generated isand (like in the game "Muck" from Dani) but i am struggeling with adding colliders to the mesh through code. It would be great if someone could help me with that. The Code is from this Video: https://www.youtube.com/watch?v=COmtTyLCd6I&list=RDCMUCmtyQOKKmrMVaKuRXz02jbQ∈dex=3
He also made a tutorial about including colliders, but he is implementing it in a script for procedural generation and i dont really need that. Anyway, here is my code where i would like to include the mesh collider script:
using UnityEngine;
using System.Collections;
public static class MeshGenerator
{
public static MeshData GenerateTerrainMesh(float[,] heightMap, float heightMultiplier,
AnimationCurve heightCurve, int levelOfDetail)
{
int width = heightMap.GetLength (0);
int height = heightMap.GetLength (1);
float topLeftX = (width -1) / -2f;
float topLeftZ = (height -1) / 2f;
int meshSimplificationIncrement = (levelOfDetail ==0)?1:levelOfDetail * 2;
int verticesPerLine = (width-1)/meshSimplificationIncrement + 1;
MeshData meshData = new MeshData(verticesPerLine, verticesPerLine);
int vertexIndex = 0;
for(int y = 0; y <height; y+= meshSimplificationIncrement)
{
for(int x = 0; x < width; x+= meshSimplificationIncrement)
{
meshData.vertices[vertexIndex] = new Vector3(topLeftX + x, heightCurve.Evaluate(
heightMap[x,y]) * heightMultiplier, topLeftZ - y);
meshData.uvs[vertexIndex] = new Vector2(x/(float)width,y/(float)height);
if(x < width -1 && y < height -1)
{
meshData.AddTriangle(vertexIndex, vertexIndex+ verticesPerLine + 1, vertexIndex +
verticesPerLine);
meshData.AddTriangle(vertexIndex + verticesPerLine + 1, vertexIndex, vertexIndex + 1);
}
vertexIndex++;
}
}
return meshData;
}
}
public class MeshData
{
public Vector3[] vertices;
public int[] triangles;
public Vector2[] uvs;
int triangleIndex;
public MeshData(int meshWidth, int meshHeight)
{
vertices = new Vector3[meshWidth * meshHeight];
uvs = new Vector2[meshWidth * meshHeight];
triangles = new int[(meshWidth-1)*(meshHeight-1)*6];
}
public void AddTriangle(int a, int b, int c)
{
triangles [triangleIndex] = a;
triangles [triangleIndex+1] = b;
triangles [triangleIndex+2] = c;
triangleIndex += 3;
}
public Mesh CreateMesh()
{
Mesh mesh = new Mesh();
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.uv = uvs;
mesh.RecalculateNormals();
return mesh;
}
}
It could be possible that the collider has to be included in one of the other scripts, but i think that should be the right one. If you can help me with this, thank you very much in advance!
Answer by andrew-lukasik · Aug 11, 2021 at 08:28 PM
Idk, easily?How do I add a MeshCollider?
MeshGeneratorTester.cs
using UnityEngine;
[RequireComponent( typeof(MeshFilter) )]
[RequireComponent( typeof(MeshRenderer) )]
[RequireComponent( typeof(MeshCollider) )]
class MeshGeneratorTester : MonoBehaviour
{
[SerializeField] int _width = 101;
[SerializeField] int _height = 101;
[SerializeField] AnimationCurve _heightCurve = new AnimationCurve( new Keyframe(0,0) , new Keyframe(1,1) );
Mesh _mesh;
#if UNITY_EDITOR
void OnValidate ()
{
if( _mesh!=null )
{
if( Application.isPlaying ) Object.Destroy( _mesh );
else Object.DestroyImmediate( _mesh );
}
float[,] heightmap = new float[_width,_height];
for( int y=0 ; y<_height ; y++ )
for( int x=0 ; x<_width ; x++ )
{
heightmap[x,y] = Random.Range( 0 , 1f );
}
var meshdata = MeshGenerator.GenerateTerrainMesh( heightmap , 10f , _heightCurve , 1 );
_mesh = meshdata.CreateMesh();
GetComponent<MeshFilter>().sharedMesh = _mesh;
GetComponent<MeshCollider>().sharedMesh = _mesh;
}
#endif
}
public static class MeshGenerator
{
public static MeshData GenerateTerrainMesh (
float[,] heightMap ,
float heightMultiplier ,
AnimationCurve heightCurve ,
int levelOfDetail
)
{
int width = heightMap.GetLength(0);
int height = heightMap.GetLength(1);
float topLeftX = (width -1) / -2f;
float topLeftZ = (height -1) / 2f;
int meshSimplificationIncrement = (levelOfDetail ==0)?1:levelOfDetail * 2;
int verticesPerLine = (width-1) / meshSimplificationIncrement + 1;
MeshData meshData = new MeshData( verticesPerLine , verticesPerLine );
int vertexIndex = 0;
for( int y=0 ; y<height ; y+=meshSimplificationIncrement )
for( int x=0 ; x<width ; x+=meshSimplificationIncrement )
{
meshData.vertices[vertexIndex] = new Vector3(
topLeftX + x ,
heightCurve.Evaluate( heightMap[x,y]) * heightMultiplier ,
topLeftZ - y
);
meshData.uvs[vertexIndex] = new Vector2(
x / (float)width ,
y / (float)height
);
if( x<width-1 && y<height-1 )
{
meshData.AddTriangle( vertexIndex , vertexIndex+verticesPerLine+1 , vertexIndex+verticesPerLine );
meshData.AddTriangle( vertexIndex+verticesPerLine+1 , vertexIndex , vertexIndex+1 );
}
vertexIndex++;
}
return meshData;
}
}
public class MeshData
{
public Vector3[] vertices;
public int[] triangles;
public Vector2[] uvs;
int triangleIndex;
public MeshData(int meshWidth, int meshHeight)
{
vertices = new Vector3[meshWidth * meshHeight];
uvs = new Vector2[meshWidth * meshHeight];
triangles = new int[(meshWidth-1)*(meshHeight-1)*6];
}
public void AddTriangle(int a, int b, int c)
{
triangles [triangleIndex] = a;
triangles [triangleIndex+1] = b;
triangles [triangleIndex+2] = c;
triangleIndex += 3;
}
public Mesh CreateMesh()
{
Mesh mesh = new Mesh();
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.uv = uvs;
mesh.RecalculateNormals();
return mesh;
}
}
Ok, so first of all thanks a lot for helping me out, I saw the answer and jumped out of the bed to test it. I coudnt get it to work though. Is there anything special I have to do or is it supposed to work like this? Maybe its because the collider has to be scaled up by 10,10,10 since the mesh is also scaled up like that, but i dont think that was the issue since not even a small meshcollider showed up. I also tried it with the scene of episode 11 of Sebastian Lagues tutorial but that also didnt work. I already got some of it working myself, but the code still has some bugs, since it spawns 5 of the colliders at a time and continues with it if the mouse touches the MapGenerator. I'll include some of that code, but i dont think its relevant since your method looks more promising.
MeshCollider meshCollider;
GameObject meshObject;
public Mesh CreateMesh()
{
Mesh mesh = new Mesh();
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.uv = uvs;
mesh.RecalculateNormals();
meshObject = new GameObject("terrainCollider");
meshCollider = meshObject.AddComponent<MeshCollider>();
meshObject.transform.localScale = new Vector3(10, 10, 10);
meshCollider.sharedMesh = mesh;
return mesh;
}
It works fine, see the attached image. What was missing is _heightCurve
set to anything (updated the code so it contains default value there)
If you disable that Mesh Renderer component you will see a green collision mesh underneath it. But word of warning: mesh colliders (non-convex) are the worst case of all kinds of colliders (costly & very low precision).
So that sadly didnt work neither. It generates a mesh collider but it looks very weird, like on the screenshot you attached up there. I also get this error message:
SendMessage cannot be called during Awake, CheckConsistency, or OnValidate UnityEngine.StackTraceUtility:ExtractStackTrace () MeshGeneratorTester:OnValidate () (at Assets/Scripts/MeshGeneratorTester.cs:28) UnityEngine.GUIUtility:ProcessEvent (int,intptr,bool&)
The best result so far was with the method I sent you up there, since I was able to get the collider working and in the right shape. But its probably in the wrong class, since it spawns way to much coliders and also wont stop with it.
Need to add these two features too:
- Only add collider while playing - automaticaly adjusting the collider to different seeds and changes too the map (since its a procedual map). If you got any ideas for these two it would be great if you could write them. This would probably save me a "few" hours. Annyway thanks a lot for your help, gotta add you too the game credits if i should ever finish that project ;)
(Second one is how it should look)
Answer by Constantin_de · Aug 17, 2021 at 04:18 PM
I finally did it!! Its a bit painfull to see how easy it actually was, but im very glad i figured it out. The only thing i did was to add this small script to my Mesh object:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class AddCollider : MonoBehaviour
{
GameObject mesh;
void Awake()
{
mesh = GameObject.Find ("Mesh");
mesh.AddComponent<MeshCollider>();
}
}
@andrew-lukasik thanks for your answers, you gave me the idea to put the script directly on the Mesh! Man, I really do love programming :DD
Your answer
Follow this Question
Related Questions
Rigidbody projectile gets stuck in two-sided Procedural mesh / mesh collider 0 Answers
Problem with colliders going through platforms 2 Answers
Problem with colliders 2 Answers
UV problem on procedural mesh generation 1 Answer
Procedural realistic terrain for hex tile map - what's causing the edges to stick up? 0 Answers