A la découverte de Flixel

Bienvenue dans cette nouvelle série de tutoriels consacrés à Flixel, une bibliothèque Open Source écrite en ActionScript 3 qui va nous permettre de créer très facilement et très rapidement des jeux de type arcade et rétro. En réalité nous pouvons même parler de moteur car Flixel embarque un tas de classes et d’outils pour nous faciliter la vie dans nos développement. Rassurez vous, vous pouvez faire ce que vous voulez comme jeu, mais Flixel est avant tout dédiée au monde des jeux rétro et arcade.

Présentation des fonctionnalités

  • Gestion avancée des Sprites et des animations de Sprites ;
  • Gestion des cartes (tilemap) de jeu au format CSV et des caméras (scrolling, etc…) ;
  • Gestion des collisions entre Sprites et entre groupes de Sprites ;
  • Gestion facile du clavier et de la souris ;
  • Enregistrement et lecture de « replay » ;
  • Possibilité de faire du « Split Screen » (écran coupé en deux pour le mode 2 joueurs) ;
  • Algorithme de Pathfinding ;
  • Possibilité d’utiliser des plugins ;
  • Et bien plus encore.

Je vous invite d’ailleurs à aller sur la page des fonctionnalités de Flixel et de tester les différentes démonstrations technique pour vous faire un avis sur les possibilité de ce petit bijoux 🙂

Enfin pour terminer cette petite introduction, voici quelques exemples de jeux réalisés avec Flixel (source flixelgames.net):

Spuck Gamma4 Entry G-Switch Grave e Tea
Essayer Spuck Gamma4 Entry Essayer G Switch Essayer Grave e Tea

Téléchargement et installation

Rendez vous sur la page de téléchargement et récupérez la version « Latest Master » qui correspond à la dernière version stable du moteur, quand vous serez plus à l’aise avec Flixel vous pourrez essayer les versions de développement (beta et dev) pour tester les dernières fonctionnalités. Nous avons vue dans le premier tutoriel consacré à ActionScript 3 comment installer une bibliothèque externe sous Windows avec FlashDevelop et sous Linux ou Mac OS X en ligne de commande, c’est donc ici exactement pareil, vous décompressez le dossier org de l’archive dans le dossier lib de votre projet, ensuite il faut mettre à jour le classpath du projet.

Une autre méthode simple consiste à copier directement le dossier org dans le dossier src de votre projet, de là vous n’avez pas besoin de mettre à jour le classpath car le moteur Flixel sera directement intégré à votre projet. Cette méthode est pratique pour les tests et les petits projets (on peut compiler très facilement en ligne de commande).

Vous pouvez donc choisir la méthode que vous voulez, dans la pratique j’utilise le dossier lib quand je travail sur un vrai projet car j’aime séparer mon code, cependant sur des petits projets ou des exemple je copie systématiquement le dossier lib dans src.

FlxGame : le point d’entrée du jeu

La première étape va être de créer un fichier Main.as dans le dossier src. Le point d’entrée de votre jeu se trouvera dans le constructeur de la classe Main qui contiendra le code nécessaire à l’initialisation de Flixel. La classe Main devra alors hériter de la classe FlxGame qui est le cœur de tout jeu reposant sur Flixel. Voyons tout de suite comment initialiser Flixel.

package
{
	import org.flixel.FlxGame;

	public class Main extends FlxGame
	{
		public function Main():void
		{
			super(800, 600, GameState, 1);
		}
	}
}

Le code est relativement simple et il n’y a presque rien à faire, on fait dériver Main de FlxGame puis on appel le constructeur de la classe mère avec les paramètres suivants :

  1. GameSizeX:uint -> Largeur de la fenêtre (800 px)
  2. GameSizeY:uint -> Hauteur de la fenêtre (600px)
  3. InitialState:Class -> Classe à instancier lors du démarrage du jeu (de type FlxState)
  4. Zoom:Number  -> (par defaut 1) Taux de zoom à appliquer

La classe à instancier au lancement doit dériver de FlxState et représentera une partie visuelle de votre jeu (le menu, l’écran de jeu, l’écran de game over, etc…), on spécifie un nom de classe et pas directement un objet car c’est Flixel qui va se charger d’instancier pour nous un nouvel objet de ce type. Dans mon exemple j’aurais donc une classe nommée GameState qui dérivera de FlxState.

Le taux de zoom est assez pratique si vous voulez faire des jeux de type « retro » avec de gros pixels, concrètement vous allez par exemple utiliser de petites images qui seront automatiquement zoomée au taux que  vous aurez spécifié.

Vous pouvez aussi spécifier d’autres paramètres au constructeur de FlxGame comme le taux de rafraîchissement désiré par exemple (fixé à 60 pour Flixel et 30 pour le lecteur Flash).

FlxState : la face visible du jeu

On va directement commencer par ajouter un nouveau fichier qu’on appellera GameState.as dans le projet (donc dans le dossier src) et créer la classe qui va bien, mais avant un peut de théorie car il faut bien.

Dans Flixel tout repose sur le système d’états, un état va représenter à un moment donné une partie (en général) visuelle de votre jeu, par exemple on aura un état pour le menu, un autre pour l’écran de jeu, un autre pour l’écran de game over, etc… Vous pouvez donc commencer à penser « Etat » et vous dire que vous aurez besoin d’un état pour :

  1. Le ou les menu(s)
  2. L’écran de chargement
  3. Le jeu en lui même
  4. L’écran de gestion des scores
  5. L’écran d’information (vous avez perdu, vous avez gagné, etc..)
  6. L’écran des crédits
  7. etc…

Un état sera au final une classe qui dérivera de FlxState et qui surchargera plusieurs méthodes. Le mécanisme utilisé par Flixel (mais aussi par beaucoup d’autres bibliothèques de jeux) repose sur le design pattern Game State ( patron de conception Etat de jeu). Le principe est le suivant, dans un premier temps on initialise les variables et les objets de travail puis on charge les ressources, ensuite on rentre dans une boucle infinie où deux méthodes seront exécutées chacune leur tour, une de mise à jour de la logique et une de mise à jour de l’affichage. Si vous avez consulté le lien que j’ai donné vous aurez surement remarqué la présence d’une méthode unload() ou cleanup() qui sert à décharger le programme des ressources non utilisés, c’est une bonne chose d’implémenter cette méthode mais si vous faites de petits jeu ça ne sera pas nécessaire dans l’immédiat. Je vous recommande vivement d’implémenter cette méthode quand vous serrez à l’aise avec Flixel mais nous en reparlerons.

Vous pouvez voir votre classe d’état comme un calque dans gimp ou photoshop, ils représente une partie d’affichage (je pense que vous avez compris à force 🙂 ). On utilisera au final plusieurs états ce qui formera le jeu.

Le pattern Game State
Fonctionnement d'un état : Le pattern Game State

On commence avant tout par surcharger la méthode create(), c’est ici que nous initialiseront les différents objets graphique comme les textes, les sprites, les sons, etc… Voyez la méthode create comme le constructeur « graphique » de la classe.

Le constructeur de la classe va servir à initialiser uniquement les variables qui n’ont pas de rapport avec Flixel (des variables de score par exemple ou des objets de l’API Flash).

Ensuite la boucle principale du jeu commence et se répète jusqu’à ce que l’état se termine (ou que l’on quitte carrément le jeu), les méthodes update() puis draw() sont alors appelées chacune leur tour.

On utilisera la méthode update() pour mettre à jour la logique du jeu, cela veut dire que si vous avez des tests de collisions à faire, des déplacements de personnage, des modifications non visuelles c’est dans cette méthode qu’il faudra les mettre.

La méthode draw() quand à elle sera chargée de mettre à jour l’affichage du jeu, donc la partie visible (afficher des sprites, afficher une map, etc…).

Pour résumer

  • public function MaClasseState() -> Constructeur de la classe qui dérive de FlxState (le nom MaClasseState est totalement arbitraire hein), il permet d’initialiser les attributs privés, protégés et publiques qui n’ont pas de rapport avec Flixel
  • public override function create():void -> Initialisation des objets en relation avec Flixel (Textes, Sprites, TileMap, etc…)
  • public override function udpate():void -> Mise à jour de la position des joueurs, des textes, tests de collisions entre Sprites, etc..
  • public override function draw():void -> Afficher sur l’écran les Sprites, les TileMap, les textes, etc…)

Je vous invite à remplir votre fichier GameState.as avec ce contenu, pour l’instant ce n’est pas énorme et ça n’affiche qu’un message (un message retro s-il vous plaît ^^ )

package
{
	import org.flixel.FlxState;
	import org.flixel.FlxText;
	import org.flixel.FlxG;

	public class GameState extends FlxState
	{
		private var m_titre:FlxText;
		private var m_texte:String;

		public function GameState()
		{
			m_texte = "Hello Flixel :)";
		}

		override public function create():void
		{
			m_titre = new FlxText(0, FlxG.height / 2, FlxG.width, m_texte);
			m_titre.setFormat(null, 24, 0x00ff00, "center", 0x005500);
			this.add(m_titre);
		}

		override public function update():void
		{
			super.update();
		}

		override public function draw():void
		{
			super.draw();
		}
	}
}

La variables m_texte est initialisée dans le constructeur car elle ne fait pas partie de Flixel, par contre m_titre est un objet de type FlxText qui lui est un objet graphique provenant de Flixel, il est donc initialisé dans la méthode create(). Comme vous l’aurez compris les objets de type FlxText permettent d’afficher du texte à l’écran. On utilise ensuite la méthode add() pour attacher m_titre à la scène (en fait cette méthode va dire à Flixel d’ajouter notre texte au stage). La méthode add() fonctionne comme le addChild() de l’API Flash (DisplayObject) à la différence que la version add() prend en paramètre un objet de type FlxObject. Dans tout vos jeux Flixel vous n’utiliserez donc que la méthode addChild().

J’aimerais revenir 15 secondes sur la différence entre le constructeur et la méthode create() car elles font toutes les deux la même chose à première vue car elles permettent toutes les deux d’initialiser des objets/variables. La méthode create est appelée après le constructeur et des traitement sont fait entre la construction de l’objet et son appel dans le programme, il est donc recommandé de l’utiliser pour initialiser les objets issus de Flixel. Ce n’est pourtant pas une convention obligatoire car vous pouvez essayer d’initialiser un FlxText dans le constructeur de la classe et ça fonctionnera, mais pour des raisons de lisibilité et de compatibilité avec les exemples du site officiel nous utiliserons create() pour les objets Flixel.

Je ne vous détail pas trop FlxText car c’est assez simple, à la construction vous spécifiez la position en X et Y par rapport au coin en haut à gauche de l’écran, la largeur de l’objet et une chaine de caractère qui est votre texte. La méthode setFormat() permet de spécifier une police d’écriture personnalisée (True Type Font), dans notre cas on garde celle d’origine donc on passe la valeur  null à la fonction. Ensuite dans l’ordre nous avons la taille du texte sur l’écran, sa couleur, son alignement et enfin la couleur d’ombrage du texte. Il existe d’autres paramètres mais je vous invite à consulter la documentation car elle est faite pour ça 😉

FlxG : la classe utilitaire incontournable

Et pour finir voici une présentation rapide de la classe FlxG, elle comporte plusieurs méthodes statiques qui vont franchement nous simplifier la vie. Depuis cette classe on peut par exemple

  • Accéder au stage (en lecture uniquement)
  • Connaître quelle touche à été pressée (elle gère les événements pour nous)
  • Connaître la position de la souris
  • Connaitre les paramètres globaux du jeu (Taille de la fenêtre, score du joueur, etc…)

Vous avez surement constaté que dans mon exemple j’utilise FlxG pour récupérer la largeur de l’écran (qui est de 800 pixels) et sa hauteur. Nous reviendrons largement sur cette classe utilitaire car on s’en servira lorsque l’on voudra déplacer des personnages au clavier, jouer des sons ou mettre à jour le score du joueur (oui Flixel gère aussi le score de l’utilisateur, elle est pas belle la vie 😉 )

Rendez vous dans le prochain tutoriel, cette fois ci nous verrons des choses plus concrètes comme déplacer et animer un personnage dans un petit décors.