Chap 6 – Édition de Niveaux, Gestion de Mémoire et Sauvegarde JSON

Introduction

Dans cet article, nous allons explorer en détail les concepts abordés dans le dernier épisode d’une série sur la création de jeux vidéo avec le langage de programmation Odin et le framework Raylib, une bibliothèque puissante pour le développement de jeux en 2D et 3D.
Nous verrons comment développer un éditeur de niveaux, gérer les fuites de mémoire, et sauvegarder/charger les niveaux en format JSON.
Chaque étape inclut des exemples de code tirés directement de la vidéo pour faciliter l’intégration dans vos projets.

Pourquoi un Éditeur de Niveaux ?

L’éditeur de niveaux est une fonctionnalité cruciale dans la création de jeux, permettant aux développeurs de placer des objets ou obstacles de façon dynamique.
Dans notre exemple, nous ajoutons la possibilité de placer des plateformes où le personnage du jeu (un chat) peut sauter et se déplacer.
Voici le code initial qui définit un simple niveau sans fonctionnalité d’édition :

struct Platform {
  pos: rl.Vector2,  // Coordonnées de la plateforme
}
struct Level {
  platforms: []Platform, // Tableau dynamique de plateformes
}
level: Level = {
  platforms = [
    Platform{ pos = rl.Vector2{ x = -20, y = 20 } },
    Platform{ pos = rl.Vector2{ x = 90, y = 50 } },
  ],
}

Ce code initialise une struct Level avec des plateformes définies de manière statique. Pour rendre l’expérience plus interactive, passons à l’ajout de capacités d’édition de niveaux.

2. Ajout de Fonctions d’édition de niveaux

Pour permettre l’édition, nous ajoutons un mode interactif activé par la touche F2. Le joueur peut alors placer ou supprimer des plateformes à l’aide de la souris. Cette fonctionnalité repose sur un tableau dynamique pour que les plateformes puissent être ajoutées ou supprimées en cours de jeu.

// Activer/désactiver le mode édition avec la touche F2
var editing: bool = false if rl.IsKeyPressed(rl.KEY_F2) {
  editing = !editing
}

Pour placer une nouvelle plateforme, nous utilisons rl.GetScreenToWorld2D de Raylib pour convertir la position de la souris en coordonnées du monde, ce qui permet un placement précis en fonction de la position du joueur dans le niveau.

// Placer une plateforme avec le clic gauche
if editing && rl.IsMouseButtonPressed(rl.MOUSE_LEFT_BUTTON) {
  mp := rl.GetScreenToWorld2D(rl.GetMousePosition(), camera)
  level.platforms = append(level.platforms, Platform{ pos = mp })
}

Pour supprimer une plateforme, nous vérifions si le curseur de la souris se trouve au-dessus d’une plateforme existante. Si oui, elle est retirée du tableau dynamique :

// Supprimer une plateforme avec le clic droit if editing && rl.IsMouseButtonPressed(rl.MOUSE_RIGHT_BUTTON) {
for i, platform in level.platforms {
  if rl.CheckCollisionPointRec(mp, PlatformCollider(platform.pos)) {
     level.platforms = remove_unordered(level.platforms, i)
     break
    }
  }
}

3. Gestion des Fuites de Mémoire

Les jeux vidéo créent souvent de nombreux objets et allouent dynamiquement de la mémoire. Si cette mémoire n’est pas libérée, elle entraîne des fuites qui peuvent ralentir le jeu, voire provoquer des plantages. Odin propose des outils pour gérer cela, notamment l’allocateur de suivi (tracking allocator). Voici comment l’utiliser :

import core.m track := m.TrackingAllocator{}
m.tracking_allocator_init(&track, context.allocator)
context.allocator = track

À la fin de l’exécution du programme, l’allocateur de suivi est détruit pour libérer toute la mémoire allouée :

defer {
  for _, entry in track.allocation_map {
    fmt.println("Memory leak detected:", entry.location, "Size:", entry.size)
  }
  m.tracking_allocator_destroy(&track)
}

4. Sauvegarde et Chargement des Niveaux avec JSON

Pour sauvegarder les données de niveau dans un fichier JSON, nous utilisons la bibliothèque JSON intégrée d’Odin. Cela rend les données facilement modifiables, car elles sont stockées dans un format lisible.

Conversion des Données de Niveau en JSON

La fonction suivante prend notre struct Level, le convertit en JSON, puis l’écrit dans un fichier nommé level.json :

import core.encoding.json import core.os  if level_data, err := json.Marshal(level, allocator = context.temp_allocator); err == nil {
os.write_entire_file("level.json", level_data) }

Chargement des Données depuis le Fichier JSON

Au démarrage, le programme lit les données JSON depuis level.json pour charger les positions de toutes les plateformes enregistrées.

if level_data, ok := os.read_entire_file("level.json", context.temp_allocator); ok {
  if err := json.Unmarshal(level_data, &level); err != nil {
    fmt.println("Error loading level:", err)
  }
}

En cas d’erreur de chargement, nous ajoutons une plateforme par défaut pour éviter de commencer avec un niveau vide :

else {
  level.platforms = append(level.platforms, Platform{ pos = rl.Vector2{ x = -20, y = 20 } })
}

5. Optimisation et Conseils sur la Mémoire Temporaire

Dans les jeux, des allocations temporaires sont souvent nécessaires pour des données qui ne persistent pas d’une frame à l’autre. Odin fournit un temp allocator pour ce type de mémoire temporaire, qui peut être nettoyée en fin de chaque exécution :

// Libération de la mémoire temporaire
defer context.temp_allocator.free_all()

Cela garantit que toute mémoire allouée temporairement durant le cycle de jeu est libérée dans la prochaine boucle d’affichage , ce qui est particulièrement utile pour des données comme des chaînes de texte affichées temporairement.

6. Conclusion et Projets Futurs

En conclusion, ce tutoriel a couvert :

  • L’ajout d’un éditeur de niveaux interactif avec un système de plateformes dynamiques.
  • La gestion efficace de la mémoire et la prévention des fuites.
  • La sauvegarde et le chargement de données de jeu en utilisant JSON pour la flexibilité et la transparence des données.

Ces techniques constituent des bases solides pour la création de jeux en Odin et Raylib. En continuant, il est possible de complexifier l’éditeur en ajoutant des éléments variés ou même des fonctionnalités de sauvegarde avancées.

By laurent

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur la façon dont les données de vos commentaires sont traitées.