Conception
Ce jeu est aussi l’occasion d’apprendre techniquement comment on réalise un tel programme en Cocoa. Cela vous aidera peut-être pour vos propres développements. Je ne dis pas que mon développement est le plus parfait, mais ici vous pourrez comprendre quels sont les difficultés qui sont ressortis, et comment met-on en place la structure du modèle du jeu, ainsi que le déploiement sur toutes les plates-formes Apple, qui sont actuellement le Mac, l’iPhone et l’iPad.
Par ou commencer, les pré-requis
Tout d’abord, avant de commencer dans le vif du code, et cela est valable pour n’importe quel programme, il faut vérifier que l’on a bien tous les éléments sur soi.
Une longue partie à été d’importer toutes les données du jeu original de Mac OS 9 vers Mac OS X. Je ne vous cache pas que cette partie est assez laborieuse et requière pas mal de temps pour récupérer toutes les images du jeu, les sons originaux et même la fonte de caractère que j’ai du réécrire à la main de A à Z! Oui, Le gardien du Savoir utilise une police de caractère hors du commun qui n’est pas une police standard que l’on trouve dans le système. J’ai utilisé un service internet gratuit qui permet de créer une fonte de caractère que vous pourrez télécharger librement : lgsfont
Autre point requis inévitable, avoir des connaissances en programmation et plus particulièrement ici en Cocoa, savoir utiliser XCode... Ce n’est pas le sujet ici, d’autres sites sont bien plus spécialisés, et si vous ne voyez pas de quoi on parle, alors passez votre chemin! Je vous conseille aussi de télécharger le fichier des sources du Gardien du Savoir pour pourvoir suivre le détail des explications ci-après.
Multiplateforme
Ainsi, on attaque directement un point sensible, mais indispensable pour avoir une bonne structure de fichier et de logique dans le code.
D’abord, pourquoi avoir choisi Cocoa, tout simplement car c’est le langage idéal et standard pour compiler des applications Mac, iPhone et iPad. Les trois plateformes tirent parti du même langage, alors pourquoi ne pas en profiter. Cependant, il s’avère que l’iPhone n’est pas un Mac, et on le voit bien dans le code, car on utilise le framework de l’iOS pour l’iPhone et celui de MacOS pour le Mac, ce n’est pas pareil! Chaque plate-forme a sa propre caractéristique: le Mac utilise des fenêtres redimensionnables et des menus, l’iPad et l’iPhone ont un écran fixe et de taille différentes et on une utilisation tactile. La façon dont on agence l’interface du jeu doit dépendre de la plateforme et de son utilisation et non l’inverse.
Ainsi on voit bien que le jeu du Gardien du Savoir est le même sur chaque plateforme, pourtant il différent dans l’interface utilisée. Tout est semblable, mais ne l’est pas??! On sent bien que cela peut-être simple, mais c’est compliqué à la fois, comment faire?
L’idée est de séparer le code pour cache plateforme, mais on ne va pas non plus faire 3 fois le même code! Il manque quelque chose d’autre... L’autre idée, c’est de prendre l’avantage sur le modèle-vue-contrôleur. C’est ces deux idées qui nous permettent d’établir notre structure de fichier.
Par la première idée, je sépare le code en créant un dossier à la racine par plateforme (en l'occurrence, Mac, iPhone et iPad). Chaque dossier contient un dossier de binaire ‘Bin’ pour les médias, un dossier ‘Project’ qui contient la solution XCode et ‘Src’ les sources du code. Tous ces dossiers crées étant bien sûr propre à la plateforme.
La deuxième idée débouche sur la création du dossier ‘Common’ à la racine, contenant un dossier de médias ‘Bin’ et sources ‘Src’ qui seront partagées en commun par toutes les plateformes. En effet modèle modèle-vue-contrôleur, keseksa? Modèle, comme le nom l’indique, c’est le modèle du Jeu, c’est le code qui sera invariant quelque soit la plateforme, par exemple, les informations du joueur font parti du modèle, une interface ‘LGSUser’ que l’on pourra partager en commun pour les 3 interfaces. Ce qui diffère entre les plateformes, c’est bien l’agencement des vues et du comportement des contrôleur par rapport à ces vues. Mais l’agencement uniquement, car pendant le jeu, le déplacement du joueur est bien le même, alors on aura aussi des contrôleurs que l’on place en commun dans notre solution.
Ainsi pour prendre l’exemple du contrôleur du Jeu, on placera le code de toutes les actions des clicks et de l’affichage dans le contrôleur ‘LGSPlatformGameViewController’. Et pendant la prise en compte des clicks, ce contrôleur fera appel à son contrôleur parent ‘LGSGameViewController’ par des méthodes communes qui vont traiter le déplacement et actions du joueur. De cette manière, et pour appuyer l’exemple, l'icône de l’ange est placé en bas de l’écran pour la version Mac, et est placé en haut à gauche pour la version iPhone, ce qui sera traité séparément dans ‘LGSPlatformGameViewController’. Cependant, l’action qui découle de l’ange, à savoir ‘afficher du texte d’aide’, est traité de façon commune dans ‘LGSGameViewController’.
Un autre exemple est celui de la carte du jeu. Celle ci est visible en permanence sur la version Mac et iPad. Par faute de place, la carte sera visible pour l’iPhone lors de l’appui sur le bouton. Le code est sensiblement le même pour le déplacement de l'icône du joueur sur la carte, mais l’affichage sera bien programmé de manière différente selon la plate-forme.
Par abus de langage, j’ai volontairement défini des constantes dans le fichier LGSTypes.h, qui facilitent la mise en commun de code. Par exemple pour une variable de vue, définie par NSView pour MacOS et par UIView pour l’iOS, je la définie en tant que LGSView. Cette définition n’est valable que dans le sens où le langage entre les plateforme est très similaire et que la fonctionnalité de la NSView est effectivement similaire à celle trouvée dans UIView, bien qu’il faut garder en tête que ce n’est évidement pas la même chose. Un iPhone n’est pas un Mac, tout comme un Mac n’est pas un iPhone.
Modèle
Le modèle de donnée est assez simple. ‘LGSData’ est l’interface qui se charge de lire et d’instancier toutes les données du jeu : les sons, les cartes et questions du jeu. Une question ‘LGSQuestion’ comprend son nombre de point (ou talents), la question elle même et les réponses possibles, dont la réponse valide. Une carte ‘LGSCard’ référence ses coordonnées sur la carte du jeu, l’image visuelle de celle-ci, les différents messages affichés, les surface clickables aboutissant à une action du jeu, ainsi que l’ensemble des actions proposée pour une carte, tel que le déplacement d’une carte à l’autre par exemple. Pendant le jeu, on crée une instance unique du joueur par ‘LGSUser’. Cette instance maintien l’état des données du joueur, tel que son nom, ses points (ou talents) et objets acquis, les questions qui lui ont été posée et quel personnage lui a posé au cours du jeu.
Pour la sauvegarde du jeu, il suffit de sérialiser l’instance de ‘LGSUser’ dans un fichier, à l’aide de NSKeyedArchiver.
Interface
L’interface affichée est peu commune à l’interface classique d’Apple. Et pour cause, ici, c’est un jeu que l’on crée! Cette interface est bien authentique à celle que vous pouvez voir sur le jeu original. Les images sont affichée classiquement par des instance de NSImageView/UIImageView. Et il a fallut mettre la main dans le cambouis pour reproduire l’interface originale de Hypercard en surchargeant la méthode drawRect: pour créer les boites de dialogue et de texte. La bibliothèque CMGlyphDrawing, m’a permis de charger la fonte de police du Gardien du Savoir et de l’appliquer dans la méthode drawRect: