Le développement d’applications multiplateformes a toujours été un calvaire pour les développeurs, notamment dans le domaine du jeu vidéo où l’on doit s’adapter aux spécificités du matériel et jongler entre les différentes API graphiques. La technologie Haxe tente de résoudre ce problème en vous permettant de compiler votre application pour de nombreuses plateformes tout en gardant la même base de code.
Haxe est un ensemble d’outils crée par Nicolas Canasse. Cet ancien Flasheur a conçu ces outils pour développer des jeux multiplateformes tout en conservant la simplicité et l’efficacité de l’API graphique de Flash. Depuis plusieurs années, cet ensemble d’outils est devenu Open Source et une fondation a même été créée pour en assurer le développement.
On distingue trois outils :
Avant toute installation, sachez que vous pouvez tester le langage avec l’éditeur en ligne de la fondation Haxe.
Vous retrouverez les instructions d’installation spécifiques à votre système d’exploitation sur le site officiel. Vous pourrez également l’installer via votre gestionnaire de paquets habituel si vous êtes sous GNU/Linux ou MacOS (via brew). Sous Windows, je vous recommande l’installation de l’IDE HaxeDevelop qui contient un logiciel de gestion de dépendances, cela vous permettra d’installer Haxe ainsi que plusieurs bibliothèques et frameworks via une interface graphique.
Pour le choix de l’éditeur, vous pourrez sûrement conserver votre chouchou vu le nombre de plugins disponible. Que vous soyez sous Emacs, VIM ou Sublime Text, vous trouverez votre bonheur sur la page dédiée.
Une fois Haxe installé et votre éditeur favori configuré en conséquence,
je vous invite à créer un fichier Main.hx
et à y copier le code suivant :
class Main {
static function main() {
trace("Hello, World!");
}
}
Le compilateur est accessible via votre terminal, placez-vous au niveau du fichier et lancez la compilation via :
haxe -main Main -js Main.js
Ce qui vous donnera un fichier Main.js
, ce dernier n’est pas fait pour
être lu par un humain, mais par curiosité nous pouvons y jeter un oeil.
// Generated by Haxe 3.3.0
(function () { "use strict";
var Main = function() {};
Main.main = function() {
console.log("Hello, World!");
};
Main.main();
})();
Vous constaterez qu’il s’agit d’un code javascript “pure”, indépendant de tout code Haxe. Il est donc exécutable seul (avec NodeJS par exemple).
node Main.js
> Hello, World!
Et si on essayait avec Python pour voir ?
main -main Main.hx -python Main.py
python Main.py
> Hello, World!
Vous pouvez vous amuser à tester les différents formats présents dans la documentation du compilateur.
Tout bon langage vient avec un ensemble de fonctions indispensables à la création de programme, Haxe n’échappe pas à la règle et nous propose une bibliothèque standard bien fournie.
Ces ensembles de fonctions sont regroupés dans des classes directement utilisables depuis votre programme. Voici un exemple pour effectuer quelques opérations mathématiques :
class Main {
static function main() {
var x = 4;
var y = 19.5;
trace(Math.sqrt(x)); // 2.0
trace(Math.abs(x)); // 4.0
trace(Math.ceil(y)); // 20
}
}
De la même manière, la librairie standard vous donne accès à un système
de tests unitaires via le package haxe.unit
.
// Création d'une classe de test
class UselessTest extends haxe.unit.TestCase {
public function testAdd() {
assertEquals(2, 1+1);
}
}
// Création d'un lanceur de test
class Main {
static function main() {
var r = new haxe.unit.TestRunner();
r.add(new UselessTest());
r.run();
}
}
haxe -main Main.hx -js Main.js
node Main.js
> Class: UselessTest .
OK 1 tests, 0 failed, 1 success
Le nombre de fonctionnalités ne s’arrête pas là : Parsing JSON/XML, fonctions Lambda, macros pour la compilation, etc. … Tout ce qui peut vous interesser est décrit dans la documentation de la librairie standard.
Afin de terminer en beauté, je vous propose de réaliser une petite animation 2D que nous testerons sur Desktop, web et mobile. Pour faciliter le développement de cette application, nous allons nous servir de OpenFL.
Il s’agit d’une API graphique inspirée de Flash dédiée au développement de jeux multiplateformes. Son utilisation est similaire à l’API graphique native de Flash, l’intérêt réside dans une bibliothèque fournie avec : Lime. Cette dernière propose un support unifié pour HTML5, Android, macOS, Linux…
Pour l’installer, rien de plus simple. Haxe possède un gestionnaire de dépendances nommé HaxeLib, la commande suivante vous permet d’installer OpenFL :
# Installation et activation de OpenFL
haxelib install openfl
haxelib run openfl setup
# On vérifie que OpenFL est bien reconnu
openfl
Utilisateurs de macOS, vous devrez surement spécifier le chemin d’installation à Haxelib. Cela se fait avec les commandes suivantes :
sudo haxelib setup /usr/local/lib/haxe/lib
sudo chmod 775 /usr/local/lib/haxe/lib
Maintenant que OpenFL est présent, nous pouvons créer un projet de la manière suivante :
openfl create project BouncingBall
La commande nous a créé la structure du projet :
.
+- BouncingBall
| +- Source
| +- Main.hx
| +- Assets
| +- BouncingBall.hxproj
| +- project.xml
L’arborescence reste assez claire, votre code ira dans le répertoire Source
et vos Assets (réalisation graphique et sonore) dans le répertoire éponyme.
Les fichiers project.xml
et Bouncingball.hxproj
sont eux des
fichiers de configuration, il vous faudra les éditer si vous souhaitez
ajouter des dépendances ou changer la configuration de vos fenêtres
(taille, FPS…).
Nous allons commencer par éditer le fichier project.xml
. Vous pouvez mettre
à jour le nom du paquet, son numéro de version, le nom de votre société, etc.
…
<!-- project.xml -->
<?xml version="1.0" encoding="utf-8"?>
<project>
<meta title="BouncingBall" package="com.synbioz.bouncingball" version="1.0.0" company="Synbioz" />
<app main="Main" path="Export" file="BouncingBall" />
<source path="Source" />
<haxelib name="openfl" />
<assets path="Assets" rename="assets" />
<!-- Parametres de la fenetre -->
<window width="800" height="800" fps="60" background="#ffffff" vsync="true" />
</project>
Pour lancer l’application, il vous suffit de passer par l’outil CLI de OpenFL :
# En version web HTML5 (disponible sur localhost:3000)
openfl test html5
# En version Desktop (avec la machine virtuelle Neko)
openfl test neko
L’export HTML5 vous donnera uniquement une page blanche pour l’instant, mais l’export Desktop devrait vous afficher une jolie fenêtre de 800x800px.
La page est un peu déprimante pour l’instant, nous allons essayer de dessiner
une balle et la faire rebondir sur les bords de l’écran. Pour faire ça
proprement, commencez par ajouter une classe Ball
dans un fichier
Ball.hx
à côté de votre Main.hx
.
// Ball.hx
package;
import openfl.display.Sprite;
// Notre objet Ball est un Sprite, un objet graphique animé
class Ball extends Sprite {
public function new() {
super();
// On prépare le tracé avec la couleur en paramètre
this.graphics.beginFill(0x000000);
// On dessine un cercle
this.graphics.drawCircle(0, 0, 50);
// Fin du tracé
this.graphics.endFill();
}
}
Nous avons maintenant une classe dont le rôle est de tracer un cercle lors de
son instanciation. Il va nous falloir ajouter un objet Ball dans notre scène.
Direction le fichier Main.hx
pour la suite des événements.
// Main.hx
package;
import openfl.display.Sprite;
class Main extends Sprite {
private var ball : Ball;
public function new () {
super();
/* On instancie un objet Ball
avant de l'ajouter à la scène courante */
ball = new Ball();
ball.x = 350;
ball.y = 350;
this.addChild(ball);
}
}
Avec ces modifications, vous devriez désormais avoir un cercle noir au milieu de l’écran. Étant donné que nous souhaitons le voir se déplacer, nous allons ajouter une fonction chargée de calculer la direction de la balle.
Avant de l’implémenter, créez deux attributs dans la classe Ball
,
un pour la vitesse et l’autre pour le mouvement (qui représente une direction).
// Ball.hx
import openfl.geom.Point;
…
public var speed : Int;
public var movement : Point;
…
public function new() {
super();
speed = 7;
movement = new Point(0, 0);
…
}
De retour dans Main.hx
, nous allons ajouter la fonction pour calculer la
direction de la balle.
// Main.hx
import openfl.geom.Point;
…
/* Calcule une nouvelle direction pour la balle */
private function bounceBall() : Void {
var randomAngle : Float = (Math.random() * Math.PI / 2) - 45;
ball.movement.x = Math.cos(randomAngle) * ball.speed;
ball.movement.y = Math.sin(randomAngle) * ball.speed;
}
Cette fonction va calculer un nouvel angle et une nouvelle direction pour la balle afin de simuler un rebond. Pour pouvoir animer la balle, il va nous falloir une fonction appelée automatiquement à chaque Frame (soit 60 fois par seconde vue notre configuration).
Pour cela, on va se servir des événements :
// Main.hx
// Ajouter l'import avant la déclaration de la classe
import openfl.events.Event;
Ajoutez le code suivant dans le constructeur de Main.hx
:
// On appel BounceBall pour avoir une direction initiale
this.bounceBall();
// On définit une fonction de callback appellée à chaque Frame
this.addEventListener(Event.ENTER_FRAME, everyFrame);
Nous avons ajouté un écouteur sur l’événement ENTER_FRAME
, autrement dit
une fonction nommée everyFrame sera appelée à chaque fois qu’une Frame
commencera.
Voici la fonction everyFrame
:
// Main.hx
private function everyFrame(event : Event) : Void {
// À chaque Frame, on met à jour les coordonnées de la balle
ball.x += ball.movement.x;
ball.y += ball.movement.y;
var limit : Int = (this.ball.width / 2);
// Si on touche un bord, on part dans l'autre sens
if (ball.x < limit || ball.x > stage.stageWidth - limit) ball.movement.x *= -1;
if (ball.y < limit || ball.y > stage.stageHeight - limit) ball.movement.y *= -1;
// Selon le bord atteint, on repositionne la balle
// et l'on calcule un nouvel angle
if (ball.movement.x < 0 && ball.x < limit) {
this.bounceBall();
ball.x = limit;
}
if (ball.movement.x > 0 && ball.x > stage.stageWidth - limit) {
this.bounceBall();
ball.x = stage.stageWidth - limit;
}
if (ball.movement.y < 0 && ball.y < limit) {
this.bounceBall();
ball.y = limit;
}
if (ball.movement.y > 0 && ball.y > stage.stageHeight - limit) {
this.bounceBall();
ball.y = stage.stageHeight - limit;
}
}
La variable limit
correspond à la moitié de la longueur
du rayon de la balle. On l’utilise dans notre gestion des collisions car
les calculs se font selon le point d’origine de l’objet, soit le centre de
la balle dans notre cas.
Si vous tentez de lancer l’application avec Neko ou HTML5, vous devriez avoir une jolie balle qui rebondit sur les bords de l’écran.
Essayons de finaliser le projet en préparant, une version release :
openfl build neko
Sous mac, vous pouvez vous rendre dans le dossier Export/mac64/neko/bin/,
vous y retrouverer un fichier .app parfaitement autonome que vous pourrez
redistribuer. Le principe sera le même pour la majorité des systèmes
d’exploitation. Sous GNU/Linux ou Windows, le dossier Export
contiendra
un fichier exécutable correspondant à votre système.
openfl build html5
Un chemin similaire vous donnera accès aux fichiers HTML et javascript.
# Si vous n'avez pas déjà les outils de développement Android.
openfl setup android
# Souvenez-vous que pour une véritable release, il vous faudra une clé PlayStore.
openfl build android
Vous avez compris la logique : vous retrouverez un .apk dans votre dossier Export.
J’espère que cette petite escapade dans le monde de Haxe vous aura plu. À l’heure où les projets de Serious Games et autres Advergames se multiplient, Haxe s’avère être un allié de poids dans la réalisation de jeux multiplateformes.
L’équipe Synbioz.
Libres d’être ensemble.