Un clicker game avec Bridge.NET.

Pour ce bonus, je vous propose de plonger un peu plus en profondeur dans Bridge.NET pour y découvrir toutes les subtilités et les meilleures manières de programmer, en étudiant un clicker game que j’ai conçu avec cette technologie.

Les règles du jeu sont simples, vous cliquez sur un bouton pour générer des tacos, et avec ceux-ci, vous achetez des restaurants pour automatiser votre business, et ainsi gagner le plus de tacos possible.

Je rendrais presque jaloux Ubisoft !

Si vous êtes ici uniquement pour regarder les sources, ou simplement jouer au jeu (pourquoi pas), il est disponible ici, gratuitement, et sans DLC (il y a quand même un battle pass).

Configuration et environnement de travail

Avant de commencer à rentrer dans le vif du sujet, je vais faire un petit point sur la configuration que j’utilise. Toute la configuration se fait dans le fichier bridge.json.

Bon déjà, par défaut, Bridge met de le résultat de la compilation (le JS) dans le répertoire bin\Debug\bridge du projet. Pour le coup, je préfère qu’il soit complètement à part, puisqu’au final, ce n’est rien de plus que le JavaScript d’un projet web qui va contenir des fichiers HTML, du CSS, des images… Je vais donc créer un répertoire TacosKing hors du projet Bridge, au même niveau que ma solution, et mettre l’output dans un répertoire dédié au JS.

  "output": "$(OutDir)/../../../TacosKing/js",

Dans mon article, j’ai expliqué que les seuls fichiers JavaScript nécessaires sont le framework bridge.js et l’output du projet : projet.js. Mais par défaut, Bridge génère tout un tas de bazar dont on se fout complètement. Pour garder les choses simples, je le configure pour qu’il ne génère que le strict minimum.

  "console": {
    "enabled": false
  },

  "sourceMap": {
    "enabled": false
  },

  "reflection": {
    "disabled": true
  },

  "html": {
    "disabled": true
  },

Je lui dis aussi de générer les fichiers en version minifié, car je ne vais pas venir les modifier, et ça fait gagner en performances. Donc let’s go.

  "outputFormatting": "Minified",

On va aussi retirer le package NewtonSoft.Json qui ne nous intéresse pas, et qui rajoute un fichier JS supplémentaire dans l’output.

Maintenant, on est ok pour la configuration. Il ne reste plus qu’a supprimer tout le JS de l’output et de le régénérer, et normalement, on ne devrait avoir que 3 fichiers JS, dont un gros fichier bridge.meta.js, mais il ne va pas nous servir, donc pas besoin de l’inclure dans index.html.

Concernant mon environnement de travail, personnellement, j’utilise Visual Studio pour toute la partie concernant Bridge (le C#), et Visual Studio Code pour le reste (HTML, CSS…) qui est, je trouve, plus approprié, et en plus, j’ai plein d’extensions sympas qui me permettent de développer plus rapidement.

J’aurais vraiment voulu utiliser VSCode pour tout le projet, mais il n’est pas totalement compatible avec .NET Framework. Il y a sûrement moyen de bidouiller, mais honnêtement, j’ai 2 écrans, et surtout la flemme.

Écran 1
Écran 2. J'utilise VSCode avec Live Server, comme ça, même pas besoin de raffraichir la page pour voir les changements.

Explication du programme

Comme on est sur quelque chose de plus complexe que ce que j’avais expliqué dans l’article, on ne va pas simplement se contenter de simples variables pour stocker toutes nos métriques. On va utiliser la POO à son plein potentiel.

Pour ça, j’ai créé une classe appelée TacosKing qui va contenir toute la logique du jeu. Tout va passer par elle : le nombre de tacos, les tacos par seconde, l’achat de restaurant…

public class TacosKing
{
    public double Tacos { get; set; }
    public double TacosPerSecond { get; set; }

    public List<TacosHouse> TacosHouses { get; set; }

    public TacosKing(List<TacosHouse> tacosHouses) { }

    public void BuyHouse(int tacosHouseIndex) { }
}
Code simplifié de la classe TacosKing.

J’ai aussi créé une classe TacosHouse, qui va représenter un type de restaurant. Elle va contenir des propriétés comme le nombre de restaurants acheté par le joueur, le prix…

public class TacosHouse
{
    public double Increase { get; }
    public double TacosPerSecond { get; }
    public double Price { get; set; }
    public int Number { get; set; }

    public TacosHouse(double increase, double tacosPerSecond, double basePrice) { }
}
Code simplifié de la classe TacosHouse.

Et dans TacosKing, je vais rajouter une liste qui va contenir les 5 types de restaurant du jeu. Chaque type sera différent par ses propriétés.

Voilà ce que ça donne à l’initialisation du jeu.

// Création de la liste de maisons
List<TacosHouse> tacosHouses = new List<TacosHouse>();
tacosHouses.Add(new TacosHouse(0.02, 0.1, 10));
tacosHouses.Add(new TacosHouse(0.02, 0.5, 50));
tacosHouses.Add(new TacosHouse(0.02, 2, 100));
tacosHouses.Add(new TacosHouse(0.02, 10, 250));
tacosHouses.Add(new TacosHouse(0.02, 50, 1000));

// Initialisation du jeu
TacosKing game = new TacosKing(tacosHouses);
Fonction Main().

Maintenant que la logique est faite, passons à l’interface du jeu. Pour cette partie, ce que je voulais éviter absolument, c’était de mélanger la logique du jeu et son UI, en l’occurrence, mettre les événements et les mises à jour des spans dans les classes TacosKing et TacosHouse. Ça n’aurait fait que de rendre le code confu.

Donc, au lieu de ça, j’ai simplement créé deux nouvelles classes : TacosKingUI et TacosHouseUI, qui vont contenir toutes les règles de l’interface, c’est-à-dire les spans, les boutons, le timer pour générer des tacos par seconde…

Voilà ce que ça donne à l’initialisation.

// Initialisation de l'UI
TacosKingUI gameUI = new TacosKingUI(game, "#tacos-number", "#tacos-per-second", "#tacos");

// UI des maisons
List<TacosHouseUI> tacosHouseUI = new List<TacosHouseUI>();
tacosHouseUI.Add(new TacosHouseUI(game, "#tacos-stand-number", "#tacos-stand-price", "#tacos-number", "#tacos-per-second", "#tacos-stand", 0));
tacosHouseUI.Add(new TacosHouseUI(game, "#tacos-truck-number", "#tacos-truck-price", "#tacos-number", "#tacos-per-second", "#tacos-truck", 1));
tacosHouseUI.Add(new TacosHouseUI(game, "#tacos-restaurent-number", "#tacos-restaurent-price", "#tacos-number", "#tacos-per-second", "#tacos-restaurent", 2));
tacosHouseUI.Add(new TacosHouseUI(game, "#tacos-factory-number", "#tacos-factory-price", "#tacos-number", "#tacos-per-second", "#tacos-factory", 3));
tacosHouseUI.Add(new TacosHouseUI(game, "#tacos-lab-number", "#tacos-lab-price", "#tacos-number", "#tacos-per-second", "#tacos-lab", 4));
Fonction Main().
Note : TacosKing est appelée dans TacosHouseUI car c’est lui qui contient la méthode BuyHouse().

Compilation

Une fois compilé, Bridge va venir simplement modifier le répertoire TacosKing/js du projet, ce qui nous donne la liberté de faire ce que l’on veut à côté. Dans le cas de Tacos King, c’est juste une page web avec du CSS, des images et du JS. Mais rien ne vous empêche d’utiliser Bridge dans un projet web MVC, ou même pour une application de bureau type Electron. En soit, tant que votre projet implique du JS front end, vous pouvez utiliser Bridge.

Pour terminer, un petit bilan sur mon ressenti personnel envers Bridge. Franchement, je trouve ça vraiment cool ! Le résultat est convainquant, et le tout en très peu de temps. Pour info, j’ai mis environ 3 heures à faire ça en ne connaissant rien de Bridge.

Je suis vraiment surpris de la fidélité entre mon code en C# et l’output JavaScript. Je me suis souvent demandé si le programme allait être capable de reproduire tel ou tel comportement (comme le timer, les listes, les dictionnaires…), et bien oui ! Pas une seule fois il ne m’a déçu.

Niveau confort de développement, hormis le fait que ça tourne encore sous .NET framework, c’est vraiment très agréable. Ca ressemble vraiment à une application console classique, ce qui va aussi ravir les amateurs de C++ et Java.

Et concernant le framework Bridge, c’est simple et plutôt bien ficelé, j’irai même jusqu’à dire que je préfère le système d’événement de Bridge à celui de JavaScript. Mais après, je suis un peu biaisé envers .NET, donc à prendre avec des pincettes :p

Donc oui, je peux vous assurer que ça tourne bien, et que c’est totalement utilisable en production.

Le seul gros bémol que j’aurais à lui reprocher, c’est la taille de son framework en JavaScript. 984ko minifié, c’est juste gigantesque. Pour comparer, JQuery ne fait que 84ko. Donc je maintiens ce que j’ai dit dans l’article, il faut que l’utilisation de Bridge soit justifiée. Pour moi, il est au top de son potentiel pour faire des SAP avec beaucoup de JavaScript, comme c’est le cas de Tacos King.

Dans tous les cas, vous pouvez retrouver les sources du clicker game ici.

Le jeu se trouve dans /TacosKing (index.html), le projet Bridge se trouve dans /Bridge. Si vous voulez essayer de recompiler le projet Bridge, pensez à restaurer les packages Nuget avant, pour récupérer toutes les dépendances.

Si vous avez le moindre souci pour faire tourner le jeu, faite moi signe dans les commentaires et je viendrais à la rescousse !