De la gestion de la météo aléatoire


Puisque les deux thèmes de la jam était “survie” et “aléatoire” il me semblait plus simple de créer un élément de gameplay qui combine les deux, et la météo est parfaite pour ça ! Survivre au froid et aux intempéries donne des éléments de gameplay intéréssant, et le rendre aléatoire oblige le joueur à anticiper l’imprévisible : vas-t-il geler cette nuit ? Dois-je prendre le risque d’explorer, ou d’attendre sagement à un feu de camp ?

Mais techniquement, comment créer une gestion crédible de la météo, tout en la randant non prévisible ? Je tiens ici à expliquer les choix principaux du développement du système, et d’en expliquer les mécanismes.

Les valeurs et le gameplay

La base du gameplay, c’est la température. J’ai décidé des valeurs suivante:

  • la plage de température valide est comprise en -15°C et 25°C
  • le joueur perd de la vie dès que la température est inférieur à -10°C
  • des modificateurs locaux peuvent ajuster la température : un feu de camp ajoute +10°C (ce qui amène à une température minimale possible de -15°C + 10°C = -5°C Il n’est donc pas possible de mourir près d’un feu, quelque soit les conditions)
  • De la buée sorts de la bouche du joueur dès que la température est inférieur à 1°C (il est alors prévenu que la température est potentiellement dangereuse)

L’autre indicateur, c’est “le temps” : pluie ou neige :

  • l’indice du temps est compris entre 0.0 et 1.0
  • il fait mauvais temps dès que la valeur est supérieur à 0.7
  • le mauvais temps est visualisé par de la pluie si la température est supérieur à 2°C ou de la neige pour toute température inférieur

Alors, comment faire varier la tempéraure et l’indice de météo, de manière aléatoire mais cohérente ?

Pourquoi le RNG (random number generator) ne marche pas

Une idée simple serait de généré un nombre aléatoire pour la météo, entre 0.0 et 1.0 pour définir la météo pour les X prochaines minutes. Ca marche ! Mais ca manque de cohérence…

Le résultat étant totalement aléatoire, on aurait quelque chose du genre:

  • Mauvais temps
  • Beau temps
  • Mauvais temps
  • Mauvais temps
  • Beau temps
  • Mauvais temps
  • Beau temps

Bref, une succession de valeurs, parfois qui se suivent, souvent différentes… Or, il serait plus agréable et réaliste d’avoir plus de “suites” de valeurs :

  • Mauvais temps
  • Beau temps
  • Beau temps
  • Beau temps
  • Mauvais temps
  • Mauvais temps
  • Mauvais temps

De plus, comment obtenir des valeurs qui évolue dans le temps de manière aléatoire, mais cohérence pour la température ? du RNG entre -15 et 25°C donnerait ce genre de résultat:

  • -10°C
  • +20°C
  • -5°C
  • 3°C
  • +5°C
  • -7°C

Aucune logique… alors qu’on devrait s’attendre à quelque chose comme:

  • +20°C
  • +5°C
  • 3°C
  • -5°C
  • -10°C
  • -7°C

** La solution par le bruit **

Si on devait mettre le RNG sous forme d’image, on obtiendrais du bruit blanc :

Il n’y a rien de cohérent… La solution, c’est le bruit de perlin !

Comme on le voit visuelement en 2D, les “formes” sont aléatoire, mais il y a bien des formes : des dégradés de couleurs entre le blanc et le noirs. Ainsi, il suffit de lire une ligne pour obtenir des valeurs qui se suivent : 0.5 0.6 0.7 0.5 0.4 0.3 0.4…

Pour que ces nombres soient aléatoire d’une partie à l’autre, il suffit de changer le “seed” du noise : c’est à dire prendre au hasard le point de départ dans l’image 2D. On lit alors tous les pixels qui suivent. Le seed est la seule valeure générée par RNG en début de partie.

Ce qui est remarquable… c’est que du coup, il est possible de prédire le temps qu’il fera ! Il suffirait de lire en avance les points avant de les jouer dans la météo.

** Intégration en jeu **

Afin d’avoir une température est une météo décorrélée, j’ai utilisé deux bruit de perlin différent pour chacun.

Il suffit de lire la valeur du bruit de perlin (dont le résultat est entre -1.0 et +1.0) et de l’ajuster à nos plage de valeurs.

_cursor += delta_time
# pour obtenir la valeur météo entre 0.0 et 1.0
var weather_index = weather_noise.get_noise_1d(_cursor) + 1.0) / 2.0 


var temperature_max = 25
var temperature_min = -15
var temperature_range = temperature_max - temperature_min  # 40

# on récupère l'index du bruit de perlin entre -1 et +1, qu'on ramène entre 0.0 et 1.0 avec la formule (valeur + 1) / 2
var temperature_index = clamp( (temperature_noise.get_noise_1d(_cursor) + 1.0) / 2.0, 0.0, 1.0) 

# interpolation linéaire de l'index entre nos valeurs de gameplay
var temperature = temperature_index * temperature_range - temperature_min 

A ce stade, la gestion de la météo est totalement aléatoire, et cohérente. Mais il est impossible de la “tweaker”. Comment faire pour obtenir plus de pluie que de beau temps ? Plus de temps froid que de temps chaud ? Ou au contraire, plus de chaud que de froid, mais le froid doit être souvent intense ?

** Pondération par les courbes de bézier **

La solution, c’est d’utiliser des courbes de bézier pour moduler le résultat.

Dans cet exemple, pour chaque valeurs en abscisse entre 0.0 et 1.0, le résultat entre 0 et 100 n’est pas proportionel ! En effet, les valeurs 0.25 et 0.75 donnent le même résultat : 50 La valeur maximale de 100 n’est atteignable qu’à l’index median 0.5. Et aux valeurs de bornes (0.0 et 1.0) on obtient 0…

Pour la température, il falait une courbe qui donne des résultat souvent au dessus de 0, des valeurs inférieur à -15 rares (les moment où l’on prend des dégats) et une zone entre 0 et -10 restreinte, pour servir d’avertissent au joueur.

Comme on le voit, la valeur médianne à 0.5 est de ~ +0°C, ce qui signifie qu’il y aurra autant de température positive que négative. Mais les valeurs dangereuses (-10°C) ne se produiront qu’à l’index inférieur à ~0.35, et dès l’index 0.25 on est déjà au minimum de -15°C ! Les chutes de températures seront donc moins fréquente que les hautes, mais plus brutale !

Mais ce n’était pas suffisant ! Car il fait plus froid la nuit que le jour… J’ai donc crée une deuxième courbe, prenant en abscisse l’heure du jour (24h ramené à un index entre 0.0 et 1.0) et en ordonnée le modificateur de température à appliquer (+5°C / -5°C ). Ainsi, on a une courbe avec un plateau de température bas la nuit, de -5°C, et un journée une interpolation plus douce jusqu’à +5°C entre midi et 14h (index de 0.5 à 0.7). La chute de température sera plus brutale en soirée !

Le code final ressemble à ceci :

_cursor += delta_time

...

var temperature_max = 25
var temperature_min = -15

# on récupère le modificateur de la journée, entre +5°C et -5°C

var temperature_daynight = temperature_day_nighy_curve.sample(daynight_cycle.time) 



# on récupère l'index du bruit de perlin entre -1 et +1, qu'on ramène entre 0.0 et 1.0 avec la formule (valeur + 1) / 2

var temperature_index = clamp( (temperature_noise.get_noise_1d(_cursor) + 1.0) / 2.0, 0.0, 1.0) 



# on ajoute nos deux température qu'on "clamp" entre nos valeurs de gameplay -15°C / +25°C

temperature = clamp(temperature_curve.sample(temperature_index) + temperature_daynight, temperature_min , temperature_max) 

Et nous voilà enfin arrivé au bout du voyage : Un système aléatoire et cohérent de la météo, tweaké au mieux pour permettre au joueur d’explorer son environement avec un bon équilibrage entre le jour… et la nuit de tous les dangers !!!

Get VikingSurvivor

Leave a comment

Log in with itch.io to leave a comment.