Que devez-vous savoir lors de la conversion d'un jeu Flash en HTML5 ?

Publié: 2022-03-10
Résumé rapide ↬ Les conseils présentés dans cet article visent à aider les développeurs de jeux HTML5 à éviter les erreurs courantes lors de la conversion de leurs jeux Flash en JavaScript, ainsi qu'à rendre l'ensemble du processus de développement aussi fluide que possible. Une connaissance de base de JavaScript, WebGL et du framework Phaser est requise.

Avec l'augmentation de l'utilisation de HTML5, de nombreuses entreprises commencent à refaire leurs titres les plus populaires pour se débarrasser de Flash obsolète et faire correspondre leurs produits aux dernières normes de l'industrie. Ce changement est particulièrement visible dans les industries du jeu/casino et du divertissement et se produit depuis plusieurs années maintenant, donc une sélection décente de titres a déjà été convertie.

Malheureusement, lorsque vous naviguez sur Internet, vous pouvez très souvent tomber sur des exemples d'un travail apparemment hâtif, ce qui se traduit par la qualité du produit final. C'est pourquoi c'est une bonne idée pour les développeurs de jeux de consacrer une partie de leur temps à se familiariser avec le sujet de la conversion Flash vers HTML5 et à apprendre quelles erreurs éviter avant de se mettre au travail.

Parmi les raisons de choisir JavaScript au lieu de Flash, outre les problèmes techniques évidents, il y a aussi le fait que changer la conception de votre jeu de SWF à JavaScript peut donner une meilleure expérience utilisateur, qui à son tour lui donne un aspect moderne. Mais comment le faire? Avez-vous besoin d'un convertisseur de jeu JavaScript dédié pour vous débarrasser de cette technologie obsolète ? Eh bien, la conversion de Flash en HTML5 peut être un jeu d'enfant - voici comment vous en occuper.

Lecture recommandée : Principes de conception de jeux HTML5

Comment améliorer l'expérience de jeu HTML5

Convertir un jeu vers une autre plate-forme est une excellente occasion de l'améliorer, de résoudre ses problèmes et d'augmenter l'audience. Voici quelques choses qui peuvent être faites facilement et qui valent la peine d'être envisagées :

  • Prise en charge des appareils mobiles
    La conversion de Flash en JavaScript permet d'atteindre un public plus large (utilisateurs d'appareils mobiles) ; la prise en charge des commandes tactiles doit également être implémentée dans le jeu. Heureusement, les appareils Android et iOS prennent désormais également en charge WebGL, de sorte qu'un rendu à 30 ou 60 FPS peut généralement être facilement atteint. Dans de nombreux cas, 60 FPS ne poseront aucun problème, ce qui ne fera que s'améliorer avec le temps, à mesure que les appareils mobiles deviennent de plus en plus performants.

  • Amélioration des performances
    Lorsqu'il s'agit de comparer ActionScript et JavaScript, ce dernier est plus rapide que le premier. En dehors de cela, la conversion d'un jeu est une bonne occasion de revisiter les algorithmes utilisés dans le code du jeu. Avec le développement de jeux JavaScript, vous pouvez les optimiser ou supprimer complètement le code inutilisé laissé par les développeurs d'origine.
  • Correction de bugs et amélioration du gameplay
    Avoir de nouveaux développeurs qui examinent le code source du jeu peut aider à corriger les bogues connus ou à en découvrir de nouveaux et très rares. Cela rendrait le jeu moins irritant pour les joueurs, ce qui les inciterait à passer plus de temps sur votre site et les encouragerait à essayer vos autres jeux.
  • Ajout d'analyses Web
    En plus de suivre le trafic, l'analyse Web peut également être utilisée pour recueillir des informations sur la façon dont les joueurs se comportent dans un jeu et où ils sont bloqués pendant le jeu.
  • Ajout de la localisation
    Cela augmenterait l'audience et est important pour les enfants d'autres pays qui jouent à votre jeu. Ou peut-être que votre jeu n'est pas en anglais et que vous souhaitez prendre en charge cette langue ?
Plus après saut! Continuez à lire ci-dessous ↓

Pourquoi ignorer HTML et CSS pour l'interface utilisateur en jeu améliorera les performances du jeu

En ce qui concerne le développement de jeux JavaScript, il peut être tentant d'exploiter HTML et CSS pour les boutons, widgets et autres éléments de l'interface graphique du jeu. Mon conseil est d'être prudent ici. C'est contre-intuitif, mais en fait, l'exploitation des éléments DOM est moins performante sur les jeux complexes et cela prend plus d'importance sur mobile. Si vous souhaitez atteindre 60 FPS constants sur toutes les plates-formes, il peut être nécessaire de renoncer à HTML et CSS.

Les éléments d'interface graphique non interactifs, tels que les barres de santé, les barres de munitions ou les compteurs de score, peuvent être facilement implémentés dans Phaser en utilisant des images régulières (la classe Phaser.Image ), en tirant parti de la propriété .crop pour le découpage et de la classe Phaser.Text pour un simple étiquettes de texte.

Des éléments interactifs tels que des boutons et des cases à cocher peuvent être implémentés à l'aide de la classe Phaser.Button . D'autres éléments plus complexes peuvent être composés de différents types simples, comme des groupes, des images, des boutons et des étiquettes de texte.

Remarque : Chaque fois que vous instanciez un objet Phaser.Text ou PIXI.Text, une nouvelle texture est créée pour y afficher le texte. Cette texture supplémentaire casse le vertex batching, alors faites attention à ne pas en avoir trop .

Comment s'assurer que les polices personnalisées ont été chargées

Si vous souhaitez afficher du texte avec une police vectorielle personnalisée (par exemple, TTF ou OTF), vous devez vous assurer que la police a déjà été chargée par le navigateur avant d'afficher du texte. Phaser v2 ne fournit pas de solution à cet effet, mais une autre bibliothèque peut être utilisée : Web Font Loader.

En supposant que vous disposiez d'un fichier de police et que vous incluiez le chargeur de polices Web dans votre page, voici un exemple simple de chargement d'une police :

Créez un fichier CSS simple qui sera chargé par Web Font Loader (vous n'avez pas besoin de l'inclure dans votre code HTML) :

 @font-face { // This name you will use in JS font-family: 'Gunplay'; // URL to the font file, can be relative or absolute src: url('../fonts/gunplay.ttf') format('truetype'); font-weight: 400; }

Définissez maintenant une variable globale nommée WebFontConfig . Quelque chose d'aussi simple que cela suffira généralement :

 var WebFontConfig = { 'classes': false, 'timeout': 0, 'active': function() { // The font has successfully loaded... }, 'custom': { 'families': ['Gunplay'], // URL to the previously mentioned CSS 'urls': ['styles/fonts.css'] } };

À la fin, n'oubliez pas de mettre votre code dans le rappel "actif" indiqué ci-dessus. Et c'est tout!

Comment faciliter la sauvegarde du jeu pour les utilisateurs

Pour stocker de manière persistante des données locales dans ActionScript, vous devez utiliser la classe SharedObject. En JavaScript, le remplacement simple est l'API localStorage, qui permet de stocker des chaînes pour une récupération ultérieure, en survivant aux rechargements de page.

La sauvegarde des données est très simple :

 var progress = 15; localStorage.setItem('myGame.progress', progress);

Notez que dans l'exemple ci-dessus, la variable progress , qui est un nombre, sera convertie en chaîne.

Le chargement est également simple, mais rappelez-vous que les valeurs récupérées seront des chaînes ou null si elles n'existent pas.

 var progress = parseInt(localStorage.getItem('myGame.progress')) || 0;

Ici, nous nous assurons que la valeur de retour est un nombre. S'il n'existe pas, alors 0 sera attribué à la variable de progress .

Vous pouvez également stocker et récupérer des structures plus complexes, par exemple, JSON :

 var stats = {'goals': 13, 'wins': 7, 'losses': 3, 'draws': 1}; localStorage.setItem('myGame.stats', JSON.stringify(stats)); … var stats = JSON.parse(localStorage.getItem('myGame.stats')) || {};

Dans certains cas, l'objet localStorage ne sera pas disponible. Par exemple, lors de l'utilisation du protocole file:// ou lorsqu'une page est chargée dans une fenêtre privée. Vous pouvez utiliser l'instruction try et catch pour vous assurer que votre code continuera à fonctionner et utilisera les valeurs par défaut, comme illustré dans l'exemple ci-dessous :

 try { var progress = localStorage.getItem('myGame.progress'); } catch (exception) { // localStorage not available, use default values }

Une autre chose à retenir est que les données stockées sont enregistrées par domaine, et non par URL. Donc, s'il y a un risque que de nombreux jeux soient hébergés sur un seul domaine, il est préférable d'utiliser un préfixe (espace de noms) lors de la sauvegarde. Dans l'exemple ci-dessus 'myGame.' est un tel préfixe et vous souhaitez généralement le remplacer par le nom du jeu.

Remarque : Si votre jeu est intégré dans une iframe, alors localStorage ne persistera pas sur iOS. Dans ce cas, vous devrez plutôt stocker les données dans l'iframe parent .

Comment tirer parti du remplacement du shader de fragment par défaut

Lorsque Phaser et PixiJS rendent vos sprites, ils utilisent un simple shader de fragment interne. Il n'a pas beaucoup de fonctionnalités car il est conçu pour une vitesse. Cependant, vous pouvez remplacer ce shader pour vos besoins. Par exemple, vous pouvez en tirer parti pour inspecter les surimpressions ou prendre en charge davantage de fonctionnalités pour le rendu.

Vous trouverez ci-dessous un exemple de la manière de fournir votre propre shader de fragment par défaut à Phaser v2 :

 function preload() { this.load.shader('filename.frag', 'shaders/filename.frag'); } function create() { var renderer = this.renderer; var batch = renderer.spriteBatch; batch.defaultShader = new PIXI.AbstractFilter(this.cache.getShader('filename.frag')); batch.setContext(renderer.gl); }

Remarque : Il est important de se rappeler que le shader par défaut est utilisé pour TOUS les sprites ainsi que lors du rendu d'une texture. N'oubliez pas non plus que l'utilisation de shaders complexes pour tous les sprites du jeu réduira considérablement les performances de rendu .

Comment changer la méthode de teinte avec un shader par défaut

Le shader par défaut personnalisé peut être utilisé pour remplacer la méthode de teinte par défaut dans Phaser et PixiJS.

La teinte dans Phaser et PixiJS fonctionne en multipliant les pixels de texture par une couleur donnée. La multiplication assombrit toujours les couleurs, ce qui n'est évidemment pas un problème ; c'est simplement différent de la teinte Flash. Pour l'un de nos jeux, nous devions implémenter une teinte similaire à Flash et avons décidé qu'un shader par défaut personnalisé pouvait être utilisé. Vous trouverez ci-dessous un exemple d'un tel shader de fragment :

 // Specific tint variant, similar to the Flash tinting that adds // to the color and does not multiply. A negative of a color // must be supplied for this shader to work properly, ie set // sprite.tint to 0 to turn whole sprite to white. precision lowp float; varying vec2 vTextureCoord; varying vec4 vColor; uniform sampler2D uSampler; void main(void) { vec4 f = texture2D(uSampler, vTextureCoord); float a = clamp(vColor.a, 0.00001, 1.0); gl_FragColor.rgb = f.rgb * vColor.a + clamp(1.0 - vColor.rgb/a, 0.0, 1.0) * vColor.a * fa; gl_FragColor.a = fa * vColor.a; }

Ce shader éclaircit les pixels en ajoutant une couleur de base à la teinte. Pour que cela fonctionne, vous devez fournir un négatif de la couleur souhaitée. Par conséquent, pour obtenir du blanc, vous devez définir :

 sprite.tint = 0x000000; // This colors the sprite to white Sprite.tint = 0x00ffff; // This gives red

Le résultat dans notre jeu ressemble à ceci (remarquez comment les chars clignotent en blanc lorsqu'ils sont touchés) :

Exemple de shader par défaut personnalisé dans le développement de jeux
Shader par défaut personnalisé (réservoirs clignotant en blanc).

Comment inspecter Overdraw pour détecter les problèmes de taux de remplissage

Le remplacement du shader par défaut peut également être utilisé pour faciliter le débogage. Ci-dessous, j'ai expliqué comment l'overdraw peut être détecté avec un tel shader.

Le surdessin se produit lorsque de nombreux ou tous les pixels de l'écran sont rendus plusieurs fois. Par exemple, de nombreux objets prenant la même place et étant rendus les uns sur les autres. Le nombre de pixels qu'un GPU peut restituer par seconde est décrit comme le taux de remplissage. Les GPU de bureau modernes ont un taux de remplissage excessif pour les besoins 2D habituels, mais les mobiles sont beaucoup plus lents.

Il existe une méthode simple pour savoir combien de fois chaque pixel à l'écran est écrit en remplaçant le shader de fragment global par défaut dans PixiJS et Phaser par celui-ci :

 void main(void) { gl_FragColor.rgb += 1.0 / 7.0; }

Ce shader éclaircit les pixels en cours de traitement. Le nombre 7.0 indique combien d'écritures sont nécessaires pour rendre le pixel blanc ; vous pouvez régler ce numéro à votre guise. En d'autres termes, les pixels plus clairs à l'écran ont été écrits plusieurs fois et les pixels blancs ont été écrits au moins 7 fois.

Ce shader aide également à trouver à la fois des objets "invisibles" qui, pour une raison quelconque, sont toujours rendus et des sprites qui ont des zones transparentes excessives autour qui doivent être supprimées (le GPU doit toujours traiter les pixels transparents dans vos textures).

Exemple du shader Overdraw en action dans le développement de jeux
Overdraw shader en action. ( Grand aperçu )

L'image de gauche montre comment un joueur voit le jeu, tandis que celle de droite affiche l'effet de l'application du shader overdraw à la même scène.

Pourquoi les moteurs physiques sont vos amis

Un moteur physique est un intergiciel chargé de simuler les corps physiques (généralement la dynamique des corps rigides) et leurs collisions. Les moteurs physiques simulent des espaces 2D ou 3D, mais pas les deux. Un moteur physique typique fournira :

  • mouvement d'objet en définissant des vitesses, des accélérations, des articulations et des moteurs ;
  • détecter les collisions entre différents types de formes ;
  • calculer les réponses de collision, c'est-à-dire comment deux objets doivent réagir lorsqu'ils entrent en collision.

Chez Merixstudio, nous sommes de grands fans du moteur physique Box2D et l'avons utilisé à quelques reprises. Il existe un plugin Phaser qui fonctionne bien à cet effet. Box2D est également utilisé dans le moteur de jeu Unity et GameMaker Studio 2.

Bien qu'un moteur physique accélère votre développement, il y a un prix que vous devrez payer : des performances d'exécution réduites. La détection des collisions et le calcul des réponses est une tâche gourmande en CPU. Vous pouvez être limité à plusieurs dizaines d'objets dynamiques dans une scène sur les téléphones mobiles ou faire face à des performances dégradées, ainsi qu'à une fréquence d'images réduite en dessous de 60 FPS.

Exemple de la différence dans la scène d'un jeu avec et sans notre superposition de débogage physique Phaser affichée en haut
Superposition de débogage physique de Phaser. ( Grand aperçu )

La partie gauche de l'image est une scène d'un jeu, tandis que le côté droit montre la même scène avec la superposition de débogage physique Phaser affichée en haut.

Comment exporter des sons à partir d'un fichier .fla

Si vous avez des effets sonores de jeu Flash dans un fichier .fla , leur exportation à partir de l'interface graphique n'est pas possible (du moins pas dans Adobe Animate CC 2017) en raison de l'absence d'option de menu à cet effet. Mais il existe une autre solution — un script dédié qui fait exactement cela :

 function normalizeFilename(name) { // Converts a camelCase name to snake_case name return name.replace(/([AZ])/g, '_$1').replace(/^_/, '').toLowerCase(); } function displayPath(path) { // Makes the file path more readable return unescape(path).replace('file:///', '').replace('|', ':'); } fl.outputPanel.clear(); if (fl.getDocumentDOM().library.getSelectedItems().length > 0) // Get only selected items var library = fl.getDocumentDOM().library.getSelectedItems(); else // Get all items var library = fl.getDocumentDOM().library.items; // Ask user for the export destination directory var root = fl.browseForFolderURL('Select a folder.'); var errors = 0; for (var i = 0; i < library.length; i++) { var item = library[i]; if (item.itemType !== 'sound') continue; var path = root + '/'; if (item.originalCompressionType === 'RAW') path += normalizeFilename(item.name.split('.')[0]) + '.wav'; else path += normalizeFilename(item.name); var success = item.exportToFile(path); if (!success) errors += 1; fl.trace(displayPath(path) + ': ' + (success ? 'OK' : 'Error')); } fl.trace(errors + ' error(s)');

Comment utiliser le script pour exporter des fichiers son :

  1. Enregistrez le code ci-dessus en tant que fichier .jsfl sur votre ordinateur ;
  2. Ouvrez un fichier .fla avec Adobe Animate ;
  3. Sélectionnez 'Commandes' → 'Exécuter la commande' dans le menu du haut et sélectionnez le script dans la boîte de dialogue qui s'ouvre ;
  4. Maintenant, un autre fichier de dialogue apparaît pour sélectionner le répertoire de destination d'exportation.

Et.. Voila! Vous devriez maintenant avoir des fichiers WAV dans le répertoire spécifié. Il ne reste plus qu'à les convertir, par exemple, en MP3, OGG ou AAC.

Comment utiliser les MP3 dans les conversions Flash vers HTML5

Le bon vieux format MP3 est de retour, car certains brevets ont expiré et maintenant tous les navigateurs peuvent décoder et lire les MP3. Cela rend le développement un peu plus facile puisqu'il n'est finalement pas nécessaire de préparer deux formats audio distincts. Auparavant, vous aviez besoin, par exemple, de fichiers OGG et AAC, alors que maintenant MP3 suffira.

Néanmoins, il y a deux choses importantes dont vous devez vous souvenir à propos du MP3 :

  • Les MP3 doivent être décodés après le chargement, ce qui peut prendre du temps, en particulier sur les appareils mobiles. Si vous voyez une pause après le chargement de tous vos éléments, cela signifie probablement que le MP3 est en cours de décodage ;
  • jouer sans interruption des MP3 en boucle est un peu problématique. La solution est d'utiliser mp3loop, dont vous pouvez lire l'article posté par Compu Phase.

Alors, pourquoi devriez-vous convertir Flash en JavaScript ?

Comme vous pouvez le constater, la conversion de Flash en JavaScript n'est pas impossible si vous savez quoi faire. Avec des connaissances et des compétences, vous pouvez arrêter de lutter avec Flash et profiter des jeux fluides et divertissants créés en JavaScript. N'essayez pas de réparer Flash — débarrassez-vous en avant que tout le monde ne soit obligé de le faire !

Vous voulez en savoir plus ?

Dans cet article, je me suis concentré principalement sur Phaser v2. Cependant, une version plus récente de Phaser est maintenant disponible, et je vous encourage fortement à la consulter, car elle a introduit une pléthore de fonctionnalités fraîches et intéressantes, telles que plusieurs caméras, scènes, tuiles ou moteur physique Matter.js.

Si vous êtes assez courageux et que vous voulez créer des choses vraiment remarquables dans les navigateurs, alors WebGL est la bonne chose à apprendre à partir de zéro. C'est un niveau d'abstraction inférieur à celui de divers frameworks ou outils de création de jeux, mais il permet d'obtenir des performances et une qualité supérieures même si vous travaillez sur des jeux ou des démos 2D. Parmi les nombreux sites Web qui pourraient vous être utiles pour apprendre les bases de WebGL, il y a WebGL Fundamentals (utilise des démos interactives). En plus de cela, pour en savoir plus sur les taux d'adoption des fonctionnalités WebGL, consultez les statistiques WebGL.

Rappelez-vous toujours qu'il n'y a pas trop de connaissances, surtout quand il s'agit de développement de jeux !