How to create a mesh at a location with code?
Hi, I´m new at C# and I´m triyng to learn more about procedural mesh generation.
I have a code where I create a landscape totally procedurally, but I want to add foliage.
I have two scripts: In the first one I create the landscape. In the second one, I get the vertices variable from the first one, and, using the location of the vertices of the mesh, I want to create one plant in each one. The loop is just for going through every vertices in the mesh.
Well, my question is how can I create a prefab in the position of this vertices.
//This is the mesh generator (the first code)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[RequireComponent(typeof(MeshFilter))]
public class TerrainProceduralGeneratorScript : MonoBehaviour
{
//Variables
Mesh mesh;
public Vector3[] vertices;
int[] triangles;
Vector2[] uvs;
public int xSize = 200;
public int zSize = 200;
public float globalAltitude = 5f;
// Start is called before the first frame update
void Start()
{
mesh = new Mesh();
GetComponent<MeshFilter>().mesh = mesh;
CreateShape();
UpdateMesh();
}
//Create the shape of the mesh
void CreateShape()
{
//Get the number of vertices depending on the size of the mesh
vertices = new Vector3[(xSize + 1) * (zSize + 1)];
//set the positions of all the vertices
for (int i = 0, z = 0; z <= zSize; z++)
{
for (int x = 0; x <= xSize; x++)
{
float y1 = Mathf.PerlinNoise(x * 1000f, z * 10f) * 1f;
float y2 = Mathf.PerlinNoise(x * 0.05f, z * 0.05f) * 1f;
float y3 = Mathf.PerlinNoise(x * 0.025f, z * 0.025f) * 2f;
float y4 = Mathf.PerlinNoise(x * 0.0125f, z * 0.0125f) * 3f;
float y = y1 * y2 * y3 * y4 * globalAltitude;
vertices[i] = new Vector3(x, y, z);
i++;
}
}
//Defines the number of triangles it must have
triangles = new int[xSize * zSize * 6];
int vert = 0;
int tris = 0;
//Create the triangles
for (int z = 0; z < zSize; z++)
{
for (int x = 0; x < xSize; x++)
{
triangles[tris + 0] = vert + 0;
triangles[tris + 1] = vert + xSize + 1;
triangles[tris + 2] = vert + 1;
triangles[tris + 3] = vert + 1;
triangles[tris + 4] = vert + xSize + 1;
triangles[tris + 5] = vert + xSize + 2;
vert++;
tris += 6;
}
vert++;
}
//Creates a UV map using all the vertices
uvs = new Vector2[vertices.Length];
for (int i = 0, z = 0; z <= zSize; z++)
{
for (int x = 0; x <= xSize; x++)
{
uvs[i] = new Vector2((float)x / xSize, (float)z / zSize);
i++;
}
}
}
//Update the mesh shape and create the mesh making it visible
void UpdateMesh()
{
//Clean the mesh
mesh.Clear();
//Create all normals of the mesh
mesh.RecalculateNormals();
//Set the vertices, triangles and UV map of the mesh
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.uv = uvs;
//Creates collision for the mesh
mesh.RecalculateBounds();
MeshCollider meshCollider = gameObject.GetComponent<MeshCollider>();
meshCollider.sharedMesh = mesh;
}
//Shows gizmos such as spheres on every vertices and lines on the normals
/*
private void OnDrawGizmos()
{
if (vertices == null)
return;
for (int i=0; i<vertices.Length; i++)
{
Gizmos.DrawSphere(vertices[i], 0.1f);
}
}
*/
}
And the second code:
//This is where I want to create the foliage prefab
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ProceduralFoliageScript : MonoBehaviour
{
//Variables
public Vector3[] terrainVertices;
void Start()
{
}
void Update()
{
//Obtain all vertices positions in the landscape mesh
GameObject terrain = GameObject.Find("ProceduralGenerator");
TerrainProceduralGeneratorScript terrainScript = terrain.GetComponent<TerrainProceduralGeneratorScript>();
terrainVertices = terrainScript.vertices;
}
void CreateFoliage()
{
//The loop pass through all the vertex positions of the mesh
for (int i = 0, z = 0; z <= zSize; z++)
{
for (int x = 0; x <= xSize; x++)
{
terrainVertices[i]
//Update the loop
i++;
}
}
}
}
And sorry if my code is bad, it´s because i've been working on other languages, but I am trying to learn C#.
And thanks in advance :)
Answer by SteenPetersen · Jun 06, 2020 at 08:43 AM
Here is a solution that I have tested as working, hope it helps and perhaps also show you some sanity checks and some good approaches to doing this sort of thing. (Note that we change your perlin noise so that we offset the perlin map, that way we get different generation every time.)
using UnityEngine;
[RequireComponent(typeof(MeshFilter))]
public class ProceduralMap : MonoBehaviour
{
//Variables
Mesh mesh;
MeshCollider _myMeshCollider;
MeshRenderer _myMeshRenderer;
[Tooltip("The material that we wish to use for the Terrain")]
[SerializeField] private Material terrainMaterial;
public Vector3[] vertices;
int[] triangles;
Vector2[] uvs;
// public properties
public int XSize { get { return _xSize; } }
[SerializeField] private int _xSize = 200;
public int ZSize { get { return _zSize; } }
[SerializeField] private int _zSize = 200;
public float GlobalAltitude { get { return _globalAltitude; } }
[SerializeField] private float _globalAltitude = 5f;
//called before Start so we can call the foliage in Start
void Awake()
{
mesh = new Mesh();
GetComponent<MeshFilter>().mesh = mesh;
CreateShape();
UpdateMesh();
}
void CreateShape()
{
//Get the number of vertices depending on the size of the mesh
vertices = new Vector3[(_xSize + 1) * (_zSize + 1)];
Vector2 offset = new Vector2(Random.Range(0, 100), Random.Range(0, 100));
//set the positions of all the vertices
// NOTE that we change this so we can get an offset on the perlin Noise map so we get different maps everytime
for (int i = 0, z = 0; z <= _zSize; z++)
{
for (int x = 0; x <= _xSize; x++)
{
float y1 = Mathf.PerlinNoise(x * 1000f + offset.x, z * 10f + offset.y) * 1f;
float y2 = Mathf.PerlinNoise(x * 0.05f + offset.x, z * 0.05f + offset.y) * 1f;
float y3 = Mathf.PerlinNoise(x * 0.025f + offset.x, z * 0.025f + offset.y) * 2f;
float y4 = Mathf.PerlinNoise(x * 0.0125f + offset.x, z * 0.0125f + offset.y) * 3f;
float y = y1 * y2 * y3 * y4 * _globalAltitude;
vertices[i] = new Vector3(x, y, z);
i++;
}
}
//Defines the number of triangles it must have
triangles = new int[_xSize * _zSize * 6];
int vert = 0;
int tris = 0;
//Create the triangles
for (int z = 0; z < _zSize; z++)
{
for (int x = 0; x < _xSize; x++)
{
triangles[tris + 0] = vert + 0;
triangles[tris + 1] = vert + _xSize + 1;
triangles[tris + 2] = vert + 1;
triangles[tris + 3] = vert + 1;
triangles[tris + 4] = vert + _xSize + 1;
triangles[tris + 5] = vert + _xSize + 2;
vert++;
tris += 6;
}
vert++;
}
//Creates a UV map using all the vertices
uvs = new Vector2[vertices.Length];
for (int i = 0, z = 0; z <= _zSize; z++)
{
for (int x = 0; x <= _xSize; x++)
{
uvs[i] = new Vector2((float)x / _xSize, (float)z / _zSize);
i++;
}
}
}
//Update the mesh shape and create the mesh making it visible
void UpdateMesh()
{
//Clean the mesh
mesh.Clear();
//Set the vertices, triangles and UV map of the mesh
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.uv = uvs;
//Creates collision for the mesh
if (_myMeshCollider == null)
{
if (TryGetComponent(out _myMeshCollider)) { }
else
{
_myMeshCollider = gameObject.AddComponent<MeshCollider>();
}
}
mesh.RecalculateBounds();
_myMeshCollider.sharedMesh = mesh;
// render the mesh
if (_myMeshRenderer == null)
{
if (TryGetComponent(out _myMeshRenderer)) { }
else
{
_myMeshRenderer = gameObject.AddComponent<MeshRenderer>();
}
}
if (terrainMaterial != null)
_myMeshRenderer.material = terrainMaterial;
else
_myMeshRenderer.material = new Material(Shader.Find("Standard"));
//Create all normals of the mesh
mesh.RecalculateNormals(); // note that we move this to he end
}
}
And then the Foliage generator:
using UnityEngine;
public class ProceduralFoliage : MonoBehaviour
{
//Variables
[SerializeField] private Vector3[] terrainVertices;
[SerializeField] private GameObject foliagePrefab;
private ProceduralMap terrainScript;
private GameObject foliageHolder; // just an empty gameObject to hold the foliage in hierarchy
void Start()
{
//Obtain all vertices positions in the landscape mesh
if(GameObject.Find("ProceduralGenerator").TryGetComponent(out terrainScript))
{
terrainVertices = terrainScript.vertices;
CreateFoliage();
}
else
{
Debug.LogError("Cannot find the terrainScript");
}
}
void CreateFoliage()
{
foliageHolder = new GameObject("foliageHolder");
if (foliagePrefab == null)
foliagePrefab = GameObject.CreatePrimitive(PrimitiveType.Capsule);
//The loop pass through all the vertex positions of the mesh
for (int i = 0, z = 0; z <= terrainScript.ZSize; z++)
{
for (int x = 0; x <= terrainScript.XSize; x++)
{
Instantiate(foliagePrefab, terrainVertices[i], Quaternion.identity, foliageHolder.transform);
i++;
}
}
}
}
You could of course also add something like this to start adding foliage randomly:
float tmp = Random.Range(0, 100);
if (tmp < chanceToSpawnPerVertex)
{
Instantiate(foliagePrefab, terrainVertices[i], Quaternion.identity, foliageHolder.transform);
foliagePositions.Add(terrainVertices[i]); // keep track of the position so we later we can spawn other things and avoid these placements.
}
i++;
Result:
Your answer
Follow this Question
Related Questions
Destroy shattered objects after being smashed. 0 Answers
use MouseOnClick two times? 1 Answer
HELP ! Highscore name input after each Death 0 Answers
Possible Missing XML/DLL Files? 0 Answers
How can I temporary increase the size of an object? 2 Answers