Unity2D – Apprentissage part 2 – Génération procédurale d’un terrain plat en 2D!

Après avoir tester le très bon Rimworld, je prends le parti de partir vers du 2D. Pourquoi? Plusieurs raisons:

  • Developper un jeu en 3D, même un prototype, demande beaucoup d’investissement sur la recherche et l’intégration de model 3d qui ne seront de toutes façons pas visuellement compatibles à la fin sans une aide exterieur.
  • La gestion de la caméra en 3D est un peu plus complexe
  • Le rendu est vraiment limitée (et nuit au gameplay) si on ne fait pas un minimum d’effort – ce que je ne suis pas prêt à faire par manque de temps –

Mon but étant d’aller assez vite vers de la génération procédurale poussée, ainsi que la mise en place de behavior (que de belles choses en perspectives!) je mise donc sur une base en 2D. Je me suis servi à nouveau de la methode de création de Mesh présenté par Quill18:

Je vous donne plus bas le code tout prêt à utiliser, mais avant ça une petite présentation / vidéo du début de projet en 2D:

Je me suis basé sur le tutorial Unity3D du roguelike afin d’avoir des bonnes pratiques pour structurer le jeu (et récupérer le visuel du personnage dans un tout premier temps)

J’ai changé le mode de caméra (suit le player) ainsi que le mode de déplacement (à la souris) et non plus case par case au clavier. Ca donne ca:

Code pour générer le terrain (sans les textures):

public void BuildMesh(GameObject instance)
{
int numTiles = sizeX * sizeY;
int numTris = numTiles * 2;
 
int vsize_x = sizeX + 1;
int vsize_y = sizeY + 1;
int numVerts = vsize_x * vsize_y;
 
Vector3[] vertices = new Vector3[numVerts];
Vector3[] normals = new Vector3[numVerts];
Vector2[] uv = new Vector2[numVerts];
 
int[] triangles = new int[numTris * 3];
 
int x, z;
for (z = 0; z < vsize_y; z++)
{
for (x = 0; x < vsize_x; x++)
{
vertices[z * vsize_x + x] = new Vector3(x * tileSize, 0, -z * tileSize);
normals[z * vsize_x + x] = Vector3.up;
uv[z * vsize_x + x] = new Vector2((float)x / sizeX, 1f - (float)z / sizeY);
}
}
 
for (z = 0; z < sizeY; z++)
{
for (x = 0; x < sizeX; x++)
{
int squareIndex = z * sizeX + x;
int triOffset = squareIndex * 6;
triangles[triOffset + 0] = z * vsize_x + x + 0;
triangles[triOffset + 2] = z * vsize_x + x + vsize_x + 0;
triangles[triOffset + 1] = z * vsize_x + x + vsize_x + 1;
 
triangles[triOffset + 3] = z * vsize_x + x + 0;
triangles[triOffset + 5] = z * vsize_x + x + vsize_x + 1;
triangles[triOffset + 4] = z * vsize_x + x + 1;
}
}
 
// Create a new Mesh and populate with the data
Mesh mesh = new Mesh();
mesh.vertices = vertices;
mesh.triangles = triangles;
mesh.normals = normals;
mesh.uv = uv;
 
MeshFilter meshFilter = instance.GetComponent<MeshFilter>();
meshFilter.mesh = mesh;
}

Une vidéo très interessante à ce propos (avec des infos supplémentaires comme le debugging de mesh, ou encore des conseils d’optimisation):