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):

Unity3D – Apprentissage part 1 – Génération procédurale d’un terrain plat!

J’ai commencé à jouer avec Unity3D ces dernières semaines.
Mon projet d’apprentissage étant une génération de map procédurale (quelle originalité!) avec en tête le fait de mettre de l’IA ensuite.

Une petite vidéo de l’état actuel:

Avec une map plus grande:

Je me sers de l’algorithme de bruit de perlin pour générer le coté random.
Je veux volontairement faire cela avec des tiles (et non pas utiliser le Terrain de Unity hyper simple à utiliser, mais hyper complexe dans les data générées)
Mon but étant d’avoir quelques niveaux d’élevation, bien distints, sur la map.

Ma première étape a été la génération du terrain plat, et la mise en place des textures. Ma premiere idée a été de mettre en place des blocs à la Minecraft mais j’ai lu que le nombre de bloc posait problème niveau performance, et puisque je ne souhaite pas « casser » le terrain dynamiquemenent, une seule mesh est préférable (quoique pas trivial à mettre en place, merci les tutoriaux youtube)

Mon deuxième (et actuel problème) est lié à la gestion des colliders. La vidéo ci dessus donne l’impression que tout fonctionne mais ca n’est en réalité pas parfait

Restaurer le focus en WPF

Si vous désirez sauver et restaurer le focus en WPF (en interagissant avec un third party par exemple) vous pouvez utiliser:

public void SetSelectedItem(object SelectedItem)
{
// Sauvegarde le focus actuel
IInputElement focusedElement = Keyboard.FocusedElement;
 
// Here, do the third party interaction, like in mvvm:
Items.Where(p=>p.Id == SelectedItem.Id).IsSelected = true; // changes the focus
 
// Restaure le focus sauvegardé
if (focusedElement != null && Keyboard.FocusedElement != focusedElement)
        Keyboard.Focus(focusedElement);
}