English version right here !
Le JS13K est un petit concours organisé chaque année pour lequel le but est de réaliser un jeu en 1 mois sur un thème donné avec une contrainte forte: ne pas dépasser 13ko une fois zippé.
Le thème de cette année était « offline ».
Le temps n’étant pas la plus grosse contrainte comme dans beaucoup de game jam, il faut redoubler d’ingéniosité pour économiser de la place.
Avant de découvrir comment le jeu a été réalisé, vous pouvez commencer par y jouer:
http://js13kgames.com/entries/alone-in-darkness
Pour les non-aficionados du tryhard, j’ai réalisé une petite vidéo de gameplay. Et pour les devs un lien vers le repo github du jeu.
J’ai terminé 130ème sur 274. Dans la première moitié du classement, mais un peu déçu au vu du travail pour trouver des mécaniques adaptées au thème, je visais le top 100 cette année.
Je pense que les mécaniques manquait de lisibilité sur l’UX (une des juges en vidéo n’a pas trouvé l’endroit pour le signal). Je ne sais pas si les autres phases de gameplay ont été testées, je n’ai eu le feedback que d’un seul juge.
Préparation
J’avais pris les devants cette année en préparant un boilerplate pour démarrer rapidement et pouvoir optimiser au mieux le code compilé. Il est dérivé du boilerplate gulp d’Adfab pour compiler le sass, minifier les sources… avec quelques tâches en plus pour tout regrouper en un seul fichier (inclure en inline le js et le css) et zip le résultat, tout en indiquant le poids du résultat pour savoir où on en est de la taille limite.
Par la suite j’ai ajouté une tache pour remplacer des mots par d’autres, pour réduire la taille occupée par le nom des classes et éléments dans le html/CSS/JS.
Le concours a commencé le 13 août, et le 15 étant férié on s’est laissé 3 jours de brainstorm pour trouver une idée originale sur le sujet.
Une des idées était une sorte de Flappy Bird avec une ligne rouge qu’on ne doit pas dépasser.
Une autre était d’être dans une maison et devoir déconnecter des objets.
Mais l’idée qui a séduit était de devoir trouver du réseau avec son téléphone pour envoyer un message, avec une inspiration survival horror.
À la base je voulais m’inspirer de Epistory pour se débarrasser des monstres, et dès le début on s’est dit que le téléphone que le personnage utilise fera partie du HUD (l’interface fixe qu’on voit à l’écran mais ne fait pas partie du jeu).
Features
Pour entrer dans le détail de tout ce qui compose le jeu, on a :
- Un téléphone en CSS qui dispose de plusieurs applications avec leurs icônes en CSS :
- Santé : pour indiquer l’état de la jauge de sprint
- Torche : pour éclairer plus loin
- Appareil photo : pour flasher et voir brièvement la carte et détecter les ombres
- Notes : pour rappeler l’objectif
- Réglages : pour rappeler les contrôles
- Musique : pour… afficher un texte indiquant que sans réseau, il n’y a pas de musique
- Signal : 5 barres pour capter le réseau
- Clavier : pour envoyer le message
- Boussole : pour trouver la sortie du niveau
- Un personnage qui peut:
- se déplacer dans 8 directions
- sprinter
- s’éclairer plus ou moins loin avec la lampe torche
- Une carte d’un niveau avec des salles et murs, avec gestion des collisions avec le joueur
- Des ombres qui suivent le personnage lorsqu’elles sont dans la lumière
🕹Le jeu se découpe en 3 phases :
- Trouver un endroit où le signal capte suffisamment
- Recopier un texte pour prévenir le monde de notre situation, tant que le signal est actif
- Trouver la sortie du niveau
Et on recommence jusqu’à ce que l’ensemble du message soit envoyé.
Journal de bord
Étant fan de tout ce qui se fait en CSS pure, comme A single div, et sachant que les images prendraient trop de place pour ce genre de concours, j’ai trouvé des modèles de téléphone en CSS pure, parfaitement adaptés.
Le design du téléphone, comprenant la boussole et les apps ne prend même pas 2ko sur le jeu.
C’est une des premières choses dont je voulais m’assurer : avoir la majorité des assets graphiques pour être sûr qu’il y aurait assez d’espace pour le code.
La gestion des déplacements est assez basique, avec une touche en plus pour le sprint.
Généralement dans les jeux de plateforme, la caméra est devant le joueur pour permettre de mieux appréhender l’environnement devant lui. Mais dans une optique de survie à l’environnement exigu, le joueur se déplace librement et la caméra ne suit le joueur que quand il s’approche du bord, pour empêcher de trop anticiper les obstacles.
Pour les collisions j’ai utilisé un algorithme que j’avais réalisé pour un projet permettant de détecter au pixel près une collision avec une couleur donnée.
Le principe est simple: on dessine la salle, puis on récupère la liste des pixels du canvas sur la zone où est le joueur. Si cette zone contient des pixels de la couleur du mur, il y a collision.
Malgré tout il y a eu quelques soucis à gérer, surtout quand le joueur sprint en diagonale dans le coin d’une pièce, pour annuler correctement le déplacement. En plus du déplacement du personnage j’ai ajouté un léger déplacement de la lampe dans la direction du joueur avec un léger délai pour plus de réalisme, et qui permet de bouger un peu les ombres même quand le joueur s’arrête.
Pour la carte du niveau j’ai commencé avec 2 rectangles, c’était largement suffisant pour valider la mécanique des ombres, lumière, déplacement et collisions.
La gestion de la lumière en lancer de rayons est fortement inspiré de cet algorithme: https://ncase.me/sight-and-light/.
J’ai revu son fonctionnement pour gérer une succession de segments et non segments par segments, pour alléger un peu les calculs, qui deviennent très coûteux avec l’augmentation du nombre de polygones.
J’ai fait quelques optimisations pour ne pas prendre en compte les salles trop éloignées, mais si un couloir était assez long, il me fallait laisser une zone suffisamment large, ainsi qu’afficher l’ombre quand on utilisait le flash.
Au final cet algorithme pourrait être encore optimisé, mais les performances restent suffisantes pour le jeu.
Par la suite, j’ai ajouté la première application sur le téléphone pour activer la lampe torche et voir plus loin.
Avant de faire la phase de recherche du signal j’ai travaillé un peu plus la map pour avoir un niveau agréable à explorer.
Au début le but était de faire une carte procédurale, générée de façon aléatoire, mais le temps a fini par trancher : la carte est restée statique.
Pour trouver le signal j’ai pensé à l’application boussole, mais un collègue m’a suggéré d’utiliser le système de barres qui indiquent la force d’un signal, beaucoup plus adapté et avec un meilleur rendu.
J’ai ensuite ajouté un clignotement du signal quand il est faible. Certains testeurs n’avaient pas compris la mécanique du signal, pas assez visible au chargement de la page. Le clignotement, qui fait ressortir cette mécanique, a amélioré la compréhension du jeu.
L’étape de saisie de message, à savoir tester s’il était simple et réactif de recopier un texte, a été une feature rapide à implémenter, et plutôt efficace.
Sur les conseils d’un collègue j’ai ajouté la petite animation de disparition des lettres une fois copiées, et quand on tape au clavier on a un feedback visuel sur les touches du téléphone dans l’interface.
J’ai gardé la boussole pour l’étape post envoi du message pour trouver la sortie du niveau. Tout en CSS l’aiguille a une animation de base autour d’un angle, qui varie entre la position du joueur et de la sortie du niveau pour le guider dans la 3ème phase.
J’ai ensuite passé plusieurs heures à skinner le personnage vu du dessus, histoire de remplacer le point rouge, avec en prime les épaules qui bougent plus ou moins vite suivant la marche ou le sprint.
La mécanique des ombres est venue assez tard puisqu’elle avait été assez mal définie au début :
- Soit elles se déplaçaient lentement et accéléraient avec la lumière en étant attirée
- Soit rapidement et éblouies par la lumière ralentissaient en s’approchant du personnage
Ces 2 options induisaient que le jeu était dans une relative pénombre pour voir où étaient les ombres, et je préférais le jeu étant entièrement dans l’obscurité. En notant que suivant le contraste et la luminosité des écrans une pénombre est compliquée à gérer correctement.
En définitive, j’ai fait en sorte que les ombres suivent le joueur à partir du moment où elles entrent dans la zone lumineuse, et s’arrêtent quand elles en sortent.
Avec la possibilité de sprinter, elles rattrapent le joueur s’il marche, et se font lors d’un sprint.
Une des idées étant aussi que le joueur active sa torche, découvre une ombre et l’éteigne pour ne pas la déranger.
L’ombre, une fois détectée, reste quant à elle visible par 2 points représentant ses yeux. Le joueur sait donc qu’il doit y faire attention.
Lors de la première détection, l’ombre commence à suivre le joueur seulement au bout d’une seconde. Cela permet de passer à côté d’une ombre sans se faire tuer immédiatement avoir un peu de temps pour réagir.
La mécanique de flash de l’appareil photo était prévue dès le début. Elle devait à la base augmenter la zone éclairée et stopper/tuer les ombres. Finalement j’ai trouvé l’idée de juste révéler leur emplacement était suffisant. Le joueur n’a pas la possibilité de s’en débarrasser mais soit juste faire attention et survivre.
S’en est suivi le design des ombres pour mieux les voir et leur donner un aspect plus fantomatique, justifiant implicitement qu’elles passent à travers les murs (dans les 13ko il aurait été compliqué d’avoir du pathfinding pour que les ombres évitent les murs).
La dernière mécanique de jeu était d’ajouter des jauges pour ne pas pouvoir sprinter ou s’éclairer à la torche en continu, ni flasher en permanence. J’avais prévu plusieurs jours pour la partie level design pour gérer correctement tous ces paramètres ainsi que les vitesses de déplacement, sprint, des ombres, la distance d’éclairage…
Malgré les idées proposées par mes collègues vers la fin du temps imparti, je me suis focalisé sur la livraison d’un jeu sans bugs pour que les gens puissent en profiter au mieux. J’ai tout de même pris le temps d’ajouter une « to do list » pour rappeler l’objectif, que j’ai présentée dans le skin des Notes iphone.
Les icônes des applications sont tous en CSS:
- 2 ronds et un carré pivoté pour le coeur ;
- Un triangle et des rectangles en box-shadow pour la lumière ;
- des boites pour l’appareil photo avec un cercle au milieu ;
- une succession de background gradient pour les notes ;
- 2 x avec un cercle au milieu pour les réglages ;
- un caractère ascii pour la musique.
Puisque l’on ne voit pas loin, il est difficile de mémoriser la carte du niveau. alors pour simuler les niveaux suivants je voulais faire une rotation ou une symétrie du même niveau. Malheureusement il fallait revoir les calcul pour la position des salles et surtout pour le calcul des ombres, et revoir les coordonnées des éléments…
J’ai ajouté une application réglages pour rappeler les contrôles, et une de musique, présente à la base pour pouvoir activer/désactiver la musique d’ambiance.
Mais pour un jeu de survie/horreur, une musique en 8 bits aurait fait bizarre, je préférais mettre une note indiquant que la musique n’est pas disponible en offline, pour rappeler le thème, et justifier son absence dans le jeu…
Et aussi parce que j’aurais dépassé les 13k ! 😅