Forums Développement Multimédia

Les formations Mediabox
Les formations Mediabox

Chapitre 7 : Un petit jeu

Par gnicos (Nicolas Gauville), le 13 juillet 2012
Navigation rapide :

<< Chapitre 6 - Sommaire - Chapitre 8 >>

De façon à voir comment nous pouvons utiliser notre moteur, nous allons maintenant réaliser un petit jeu avec ! Attention cependant : il s'agit d'un début de jeu, simple et rapide, destiné à vous aider à vous rendre compte de comment on peu simplement et efficacement utiliser le moteur. Ceci dit : vous pourrez ainsi continuer ce jeu à la suite si vous le souhaitez, je vous donnerai d'ailleurs quelques pistes pour aller plus loin.

1. Le concept du jeu

Le jeu que nous allons réaliser sera vraiment très simple. Au lancement, nous aurons une simple bouton “Jouer” :

Une fois cliqué, nous aurons une petite carte isométrique, avec le personnage contrôlable au clavier :

Le principe du jeu est simple :

  1. - Le joueur peut se déplacer dans les 4 directions. A chaque fois qu'il quitte une tuile, celle-ci est détruite (et disparaît donc de la carte).
  2. - Si le joueur “tombe” dans le vide (se rend à un emplacement vide), la partie est perdue.
  3. - Pour gagner, il ne doit plus rester aucune tuile (à l'exception de celle ou le joueur se trouve).

Dans l'exemple que j'ai choisi, ce n'est vraiment pas difficile de gagner. Mais nous pouvons ensuite imaginer d'autres cartes plus complexes de façon à obtenir un veritable casse-tête.

Enfin, une fois la partie gagnée ou perdue, nous affichons à nouveau le bouton jouer, avec un message indiquant le résultat :

2. Ce dont nous avons besoin

J'ai utilisé un bouton “Jouer” que j'ai dessiné sur Flash, et exporté via “Export for CreateJS”. Bien sur, j'ai réécris une partie du code obtenu pour le rendre plus clair et plus simple, mais la partie graphique (traçage du contour) est restée telle quelle, ayant utilisé la compression.

Libre à vous d'utiliser le même que moi ou d'en re-dessiner un ; je vous laisse tout de même le code du mien ici :

gfx.play = function()
{
        this.text = new Text("Jouer", "30px Arial Black", "#ffff00");
        this.text.textAlign = "center";
	this.text.textBaseline = "top";
	this.text.lineHeight = 32;
	this.text.lineWidth = 134;
	this.text.setTransform(71,0.8);
	this.text.shadow = new Shadow("#ff0000",0,0,10);
 
	this.gfx = new Shape();
	this.gfx.graphics.lf(["#ff6600","#ffff00"],[0,1],-11.6,-35.2,-11.6,28.7)
                .p("AKNi2QqciHp2CHQiBC0CBCzQJTCVK/iVQB1ikh1jD").f()
                .s("#ff6600").ss(2.5,1,1,3)
                .p("AKNi2QB1DDh1CkQq/CVpTiVQiBizCBi0QJ2iHKcCH").cp();
        this.gfx.setTransform(71.1,25.1);
 
        this.container = new Container ();
        this.container.addChild ( this.gfx, this.text );
 
	return this.container;
};

Ce code étant, bien entendu, à insérer dans le fichier “gfx.js”.

3. Mise en place

Nous allons donc garder les fichiers du moteur (dossier “src”), le fichier “gfx.js” (après avoir ajouté le bouton), et modifier le fichier “core.js” pour réaliser notre jeu.

Le fichier sera organisé comme suit :

  1. - On définit toutes les variables de base.
  2. - Une fonction “onKeyDown ($e)” pour gérer les évènements clavier.
  3. - Une fonction “menu ($text)” qui supprimera le jeu (si il y avait un jeu) et affichera le menu, avec, éventuellement, le texte (gagné ou perdu).
  4. - Une fonction qui se déclenche lorsque l'on appuie sur le bouton “Jouer” : elle supprimera donc le menu, et lancera le jeu.

a) Initialisation

On commence comme d'habitude par l'initialisation de l'objet “stage” :

/**
 * Initialisation.
 */
var gameview = document.getElementById('gameview'), stage;
stage = new Stage(gameview);
Ticker.setFPS(24);
Ticker.addListener(stage);

On définit, comme les dernière fois, une variable “map” qui définira la carte. Ici, j'ai choisit d'utiliser les “0” pour les tuiles, les “1” pour “rien”. Bien sur, vous pouvez changer et utiliser la carte que vous voulez.

/**
 * Carte du jeu.
 */
var map = [     [1, 1, 0, 0, 0],
                [0, 0, 0, 0, 0],
                [0, 0, 1, 0, 1],
                [0, 0, 1, 0, 0],
                [0, 0, 0, 0, 0]];

On définit la variable “myMap”. Cette fois, on ne lui donne pas de valeur, nous le feront lors du clic sur le bouton “jouer” :

var myMap = null;

Pour le menu, nous aurons besoin de deux variables : le texte et le bouton play. Le bouton peut être créé et placé, il suffira de l'ajouter/retirer de la liste d'affichage :

/**
 * Menu.
 */
var play = gfx.play();
play.x = 340;
play.y = 268;

Enfin, pour le texte par contre, nous le créerons en fonction du texte à afficher, nous déclarons donc juste la variable :

var text = null;

Pour l'initialisation, nous avons donc le code suivant :

/**
 * Initialisation.
 */
var gameview = document.getElementById('gameview'), stage;
stage = new Stage(gameview);
Ticker.setFPS(24);
Ticker.addListener(stage);
 
/**
 * Carte du jeu.
 */
var map = [     [1, 1, 0, 0, 0],
                [0, 0, 0, 0, 0],
                [0, 0, 1, 0, 1],
                [0, 0, 1, 0, 0],
                [0, 0, 0, 0, 0]];
var myMap = null;
 
/**
 * Menu.
 */
var play = gfx.play();
play.x = 340;
play.y = 268;
var text = null;

b) Fonction "onKeyDown"

Nous allons maintenant passer à la méthode “onKeyDown”. Cette fois, cette méthode sera organisée différemment. En effet, cette fois, les déplacements pourrons déclencher la fin d'une partie (plus de tuile, ou déplacement vers une tuile vide), nous allons donc devoir être plus prudents.

Nous allons commencer par définir une variable “current_tile”, correspondant à la tuile sur laquelle le joueur se trouve, dans la mesure ou si le déplacement est effectué, cette tuile sera supprimée.

/**
 * Gestion des déplacements clavier.
 */
function onKeyDown ($e)
{        
        current_tile = myMap.getTileAt ( player.posX, player.posY, 0 );
}

Nous allons ensuite utiliser un switch sur la propriété “$e.keyCode” comme la dernière fois, à la différence que cette fois, nous n'utiliserons plus la méthode “move” de notre classe “player”. Cette fois, nous allons définir une variable “to_tile”, qui correspondra à la tuile sur laquelle nous souhaitons aller. Bien entendu, nous ne somme pas sur que cette tuile existe, elle peut valoir “null”, cas dans lequel la partie est perdue.

Nous avons donc le code suivant :

switch ( $e.keyCode )
{
        case 37: //Gauche
                to_tile = myMap.getTileAt ( player.posX, player.posY - 1, 0 );
                break;
        case 38: //Haut
                to_tile = myMap.getTileAt ( player.posX - 1, player.posY, 0 );
                break;
        case 39: //Droite
                to_tile = myMap.getTileAt ( player.posX, player.posY + 1, 0 );
                break;
        case 40: //Bas
                to_tile = myMap.getTileAt ( player.posX + 1, player.posY, 0 );
                break;
        default:
                return;
                break;
}

Ensuite, nous avons deux cas possibles : soit la variable “to_tile” existe, et le déplacement doit donc être effectué. Sinon, la partie est perdue :

if ( to_tile )
{
        //On effectue le déplacement
}
else
{
        menu ( "Perdu !!" );
}

Nous allons donc nous intéresser à la partie “Effectuer le déplacement”. Nous commençons donc par assigner les coordonnées de la tuile “to_tile” au joueur :

player.posX = to_tile.posX;
player.posY = to_tile.posY;

On met ensuite à jour la carte :

myMap.update();

Et on n'oublie pas de supprimer la tuile que l'on vient de quitter :

myMap.removeTile ( current_tile );

Enfin, dernière chose à faire : vérifier que le joueur n'a pas gagné. Si c'est le cas, il ne restera donc plus que deux tuiles : le joueur et le sol sur lequel il se trouve. Nous ajoutons donc une petite condition :

if ( myMap.tiles.length === 2 )
{
        menu ( "Bravo !" );
}

Et … nous avons alors finis notre fonction “onKeyDown” ! Je récapitule tout de même le code de cette fonction :

/**
 * Gestion des déplacements clavier.
 */
function onKeyDown ($e)
{        
        current_tile = myMap.getTileAt ( player.posX, player.posY, 0 );
 
        switch ( $e.keyCode )
        {
                case 37: //Gauche
                        to_tile = myMap.getTileAt ( player.posX, player.posY - 1, 0 );
                        break;
                case 38: //Haut
                        to_tile = myMap.getTileAt ( player.posX - 1, player.posY, 0 );
                        break;
                case 39: //Droite
                        to_tile = myMap.getTileAt ( player.posX, player.posY + 1, 0 );
                        break;
                case 40: //Bas
                        to_tile = myMap.getTileAt ( player.posX + 1, player.posY, 0 );
                        break;
                default:
                        return;
                        break;
        }
 
        if ( to_tile )
        {
                player.posX = to_tile.posX;
                player.posY = to_tile.posY;
 
                myMap.update();
                myMap.removeTile ( current_tile );
 
                if ( myMap.tiles.length === 2 )
                {
                        menu ( "Bravo !" );
                }
        }
        else
        {
                menu ( "Perdu !!" );
        }
}

c) Fonction "menu"

Nous commençons donc par supprimer l'éventuel “jeu” en cours. Evidement, il faut d'abord vérifier qu'il y a un jeu à supprimer. Pour cela, nous allons nous fier au paramètre “$text”. Soit cette variable est nulle, et donc il n'y a pas encore eu de jeu (pas de texte de fin de partie à afficher), soit elle n'est pas nulle, et donc nous devons supprimer le jeu :

function menu ( $text )
{
        /**
         * On supprime le jeu si besoin.
         */
        if ( $text !== "" )
        {
                /**
                 * Il y a un texte à afficher (gagné ou perdu), donc un jeu a supprimer.
                 */
 
                /**
                 * On affiche le text.
                 */
        }
 
        /**
         * On affiche le menu.
         */
}

Pour supprimer l'éventuel jeu en cours, nous avons trois choses à faire :

  1. - Supprimer la carte de la liste d'affichage.
  2. - Supprimer la carte (méthode “dispose”).
  3. - Supprimer l'écouteur pour la fonction “onKeyDown”.

Nous avons donc le code suivant :

stage.removeChild ( myMap );                                                           
myMap.dispose ();
document.removeEventListener ( 'keyup', onKeyDown );

Ensuite, il faut évidemment afficher le texte donné dans la variable “$text”. Pour cela, nous utilisons la classe “Text” de CreateJS. Nous créons donc l'objet text :

text = new Text($text, "30px Arial Black", "#ffff00");

Nous choisissons l'alignement :

text.textAlign = "center";
text.textBaseline = "top";

Nous plaçons notre code :

text.x = 408;
text.y = 228;

Et enfin, nous l'ajoutons à la liste d'affichage :

stage.addChild ( text );

Enfin, pour terminer notre fonction, nous ajoutons le bouton “play” à la liste d'affichage (quelque soit la variable “$text”) :

/**
 * On affiche le menu.
 */
stage.addChild ( play );

Notre fonction “menu” est donc comme suit :

function menu ( $text )
{
        /**
         * On supprime le jeu si besoin.
         */
        if ( $text !== "" )
        {
                /**
                 * Il y a un texte à afficher (gagné ou perdu), donc un jeu a supprimer.
                 */
                stage.removeChild ( myMap );                                                           
                myMap.dispose ();
                document.removeEventListener ( 'keyup', onKeyDown );
 
                /**
                 * On affiche le text.
                 */
                text = new Text($text, "30px Arial Black", "#ffff00");
                text.textAlign = "center";
                text.textBaseline = "top";
                text.x = 408;
                text.y = 228;
                stage.addChild ( text );
        }
 
        /**
         * On affiche le menu.
         */
        stage.addChild ( play );
}

d) Bouton "jouer"

Dernière fonction : celle qui se déclenche lorsque nous appuyons sur le bouton “jouer”. Cette fonction va donc supprimer le menu, et lancer le jeu :

play.onClick = function ( $e )
{
        /**
         * On supprime le menu.
         */
 
        /**
         * On lance le jeu.
         */
}

Pour supprimer le menu, nous commençons par supprimer le bouton “play” de la liste d'affichage :

stage.removeChild ( play );

Ensuite, s'il y a un texte, nous le supprimons :

if ( text )
{
        stage.removeChild ( text );
        text = null;
}

Nous devons ensuite initialiser le jeu. Nous commençons par définir et placer la carte :

/**
 * On lance le jeu.
 */
myMap = new Map ();
myMap.offsetX = 400;
myMap.offsetY = 200;
stage.addChild ( myMap );

Puis … le joueur !

player = new Player ( TileType.DRAW, gfx.mur ( 255, 0, 0 ), true );
myMap.addTile ( player, 3, 3, 1 );

Nous plaçons ensuite les tuiles, avec, comme d'habitude, une double boucle for. La seule différence, c'est, qu'ici, nous ajoutons une tuile si la valeur de la case de la variable “map” est 0, et, sinon, nous ne faisons rien :

for ( var i = 0; i < 5; i++ )
{
        for ( var j = 0; j < 5; j++ )
        {
                if ( map[i][j] === 0 )
                {
                        myMap.addTile ( new Tile ( TileType.DRAW, gfx.solTAlea(200,200,128), true ), i, j, 0 );
                }
        }
}

Enfin, nous ajoutons l'écouteur pour la fonction “onKeyDown” :

document.addEventListener ( 'keyup', onKeyDown );

Notre fonction est donc définie ainsi :

play.onClick = function ( $e )
{
        /**
         * On supprime le menu.
         */
        stage.removeChild ( play );
 
        if ( text )
        {
                stage.removeChild ( text );
                text = null;
        }
 
        /**
         * On lance le jeu.
         */
        myMap = new Map ();
        myMap.offsetX = 400;
        myMap.offsetY = 200;
        stage.addChild ( myMap );
 
        player = new Player ( TileType.DRAW, gfx.mur ( 255, 0, 0 ), true );
        myMap.addTile ( player, 3, 3, 1 );
 
        for ( var i = 0; i < 5; i++ )
        {
                for ( var j = 0; j < 5; j++ )
                {
                        if ( map[i][j] === 0 )
                        {
                                myMap.addTile ( new Tile ( TileType.DRAW, gfx.solTAlea(200,200,128), true ), i, j, 0 );
                        }
                }
        }
 
        document.addEventListener ( 'keyup', onKeyDown );
}

d) Ultime étape

Enfin, l'ultime étape, très rapide, mais indispensable : lancer le menu :

menu ( "" );

4. Rendu final

Le code du fichier “core.js” est donc le suivant :

/**
 * Initialisation.
 */
var gameview = document.getElementById('gameview'), stage;
stage = new Stage(gameview);
Ticker.setFPS(24);
Ticker.addListener(stage);
 
/**
 * Carte du jeu.
 */
var map = [     [1, 1, 0, 0, 0],
                [0, 0, 0, 0, 0],
                [0, 0, 1, 0, 1],
                [0, 0, 1, 0, 0],
                [0, 0, 0, 0, 0]];
var myMap = null;
 
/**
 * Menu.
 */
var play = gfx.play();
play.x = 340;
play.y = 268;
var text = null;
 
 
/**
 * Gestion des déplacements clavier.
 */
function onKeyDown ($e)
{        
        current_tile = myMap.getTileAt ( player.posX, player.posY, 0 );
 
        switch ( $e.keyCode )
        {
                case 37: //Gauche
                        to_tile = myMap.getTileAt ( player.posX, player.posY - 1, 0 );
                        break;
                case 38: //Haut
                        to_tile = myMap.getTileAt ( player.posX - 1, player.posY, 0 );
                        break;
                case 39: //Droite
                        to_tile = myMap.getTileAt ( player.posX, player.posY + 1, 0 );
                        break;
                case 40: //Bas
                        to_tile = myMap.getTileAt ( player.posX + 1, player.posY, 0 );
                        break;
                default:
                        return;
                        break;
        }
 
        if ( to_tile )
        {
                player.posX = to_tile.posX;
                player.posY = to_tile.posY;
 
                myMap.update();
                myMap.removeTile ( current_tile );
 
                if ( myMap.tiles.length === 2 )
                {
                        menu ( "Bravo !" );
                }
        }
        else
        {
                menu ( "Perdu !!" );
        }
}
 
function menu ( $text )
{
        /**
         * On supprime le jeu si besoin.
         */
        if ( $text !== "" )
        {
                /**
                 * Il y a un texte à afficher (gagné ou perdu), donc un jeu a supprimer.
                 */
                stage.removeChild ( myMap );                                                           
                myMap.dispose ();
                document.removeEventListener ( 'keyup', onKeyDown );
 
                /**
                 * On affiche le text.
                 */
                text = new Text($text, "30px Arial Black", "#ffff00");
                text.textAlign = "center";
                text.textBaseline = "top";
                text.x = 408;
                text.y = 228;
                stage.addChild ( text );
        }
 
        /**
         * On affiche le menu.
         */
        stage.addChild ( play );
}
 
play.onClick = function ( $e )
{
        /**
         * On supprime le menu.
         */
        stage.removeChild ( play );
 
        if ( text )
        {
                stage.removeChild ( text );
                text = null;
        }
 
        /**
         * On lance le jeu.
         */
        myMap = new Map ();
        myMap.offsetX = 400;
        myMap.offsetY = 200;
        stage.addChild ( myMap );
 
        player = new Player ( TileType.DRAW, gfx.mur ( 255, 0, 0 ), true );
        myMap.addTile ( player, 3, 3, 1 );
 
        for ( var i = 0; i < 5; i++ )
        {
                for ( var j = 0; j < 5; j++ )
                {
                        if ( map[i][j] === 0 )
                        {
                                myMap.addTile ( new Tile ( TileType.DRAW, gfx.solTAlea(200,200,128), true ), i, j, 0 );
                        }
                }
        }
 
        document.addEventListener ( 'keyup', onKeyDown );
}
 
menu ( "" );

Et voici le zip contenant la totalité du code :

Fichier zip : tiles_game.zip

5. Pour aller plus loin

Nous avons donc un petit début de jeu. Bien entendu, les moyens de l'améliorer sont nombreux, mais voici quelques idées :

Un vrai menu

Vous pourriez, bien sur, commencer à améliorer le menu. Ajouter un fond, éventuellement animé, et ajouter des effets sur le bouton “jouer”.

Eventuellement, ajouter une partie “Comment jouer” indiquant les règles du jeu.

Des animations !

C'est probablement ce qui manque le plus : les animation. Ajouter des animations lorsque le joueur se déplace, et lorsqu'une tuile disparaît serais assez intéressant.

D'autres niveaux !

Enfin, le dernier “gros” point à améliorer, serais d'ajouter d'autres niveaux, de plus en plus difficiles.

Si vous partez de cette base pour améliorer le jeu, pensez à en parler sur le forum ;).

6. Conclusion

Nous avons ainsi pu voir comment utiliser le moteur pour réaliser un début de jeu. Dans le prochain chapitre, nous aborderons “l'emballage” du moteur !

Navigation rapide :

<< Chapitre 6 - Sommaire - Chapitre 8 >>