Un jeu HTML5 : menu et collisions

Dans ce chapitre nous allons voir le menu de notre jeu et mettre en place la gestion des collisions.

Les références

On va avoir les nouveaux fichiers menu.js et collisions.js, on va donc les charger dans index.html :

<!-- Load Crazy Bird -->
<script src="js/game.js"></script>
<script src="js/boot.js"></script>
<script src="js/particles.js"></script>
<script src="js/load.js"></script>
<script src="js/settings.js"></script>
<script src="js/utils.js"></script>
<script src="js/menu.js"></script>
<script src="js/collisions.js"></script>

D’autre part dans le fichier load.js on avait commenté les événements pour éviter de générer des erreurs, on va avoir besoin maintenant d’en dé-commenter quelques uns :

...
C.sprites.help.mousedown = C.menu.onHelp;
C.sprites.help.touchstart = C.menu.onHelp;
...
C.helpPannel.mousedown = C.menu.onHideHelp;
C.helpPannel.touchstart = C.menu.onHideHelp; 
...
C.sprites.play.mousedown = C.menu.onPlay;
C.sprites.play.touchstart = C.menu.onPlay;
...

Enfin dans le fichier game.js on va ajouter les propriétés nécessaires pour le jeu, on en est donc avec toutes ces propriétés :

// Properties
state: undefined,
stage: undefined,
renderer: undefined,
pannelHeight: 40,
sounds : {
    balloon: undefined,
    crash: undefined,
    crashPlane: undefined,
    life: undefined        
},
sprites: {
    loading : undefined,
    sky: undefined,
    land: undefined,
    title: undefined,
    play: undefined,
    help: undefined,
    bird: undefined,
    pannel: undefined,
    planes: [],
    balloons: [],
    gameOver: undefined,
    playAgain: undefined
},
helpPannel: undefined,
helpMessage: undefined,
highScoreText: undefined,
messageText: undefined,
nbrPlanes: 3,
nbrBalloons: 2,
elapsed: Date.now(),
level: 1,
balloonsDone: 0,
maxBalloons: 8,
lifes: 0,
score: 0,
speed: 5,

Le fichier menu.js

C’est ici qu’on a le code pour gérer le menu auquel on a abouti dans le précédent chapitre :

/* global PIXI, C */

C.menu = {
    
    // Properties
    scaleStep: 0.01,
    
    show: function () {
        C.sprites.title.scale.x += this.menu.scaleStep;
        C.sprites.title.scale.y += this.menu.scaleStep;
        if (C.sprites.title.scale.x > 1.1) {
            this.menu.scaleStep = -.01;
        } else if (C.sprites.title.scale.x < .9) {
            this.menu.scaleStep = .01;
        }
    },
    
    // Help event
    onHelp: function () {
        C.helpPannel.visible = true;
        C.helpMessage.visible = true;
    },
    
    // Hide help event
    onHideHelp: function () {
        C.helpPannel.visible = false;
        C.helpMessage.visible = false;
    },
    
    // Play event
    onPlay: function () {
        C.sprites.title.visible = false;
        C.sprites.play.visible = false;
        C.sprites.help.visible = false;
        C.highScoreText.visible = false;
        C.sprites.bird.visible = true;
        C.messageText.visible = true;
        C.sprites.pannel.visible = true;
        C.state = C.play.on;
    }
};

La fonction show

On a vu que l’état dans lequel le jeu se trouve appelle en permanence cette fonction. En effet dans setting.js on a prévu à la fin :

C.state = C.menu.show;

 On fait un effet de changement de dimension du titre (il grossit et se réduit de façon cyclique). A cet effet on prévoit une propriété :

scaleStep: 0.01,

 Et ensuite on gère la dimension dans la fonction :

show: function () {
    C.sprites.title.scale.x += this.menu.scaleStep;
    C.sprites.title.scale.y += this.menu.scaleStep;
    if (C.sprites.title.scale.x > 1.1) {
        this.menu.scaleStep = -.01;
    } else if (C.sprites.title.scale.x < .9) {
        this.menu.scaleStep = .01;
    }
},

 Ça crée un peu d’animation sur l’écran et c’est facile à faire ! on en est ici :

img07

Help !

Voyons maintenant la partie aide. Pour faire apparaître la fenêtre d’aide il faut cliquer (ou toucher) sur « HELP ! ». Ce qui appelle la fonction onHelp :

// Help event
onHelp: function () {
    C.helpPannel.visible = true;
    C.helpMessage.visible = true;
},

 On se contente de rendre visible le panneau d’aide :

img08

Si on clique (ou touche) maintenant le panneau d’aide on appelle la fonction onHideHelp :

// Hide help event
onHideHelp: function () {
    C.helpPannel.visible = false;
    C.helpMessage.visible = false;
},

Et là on rend à nouveau le panneau non visible.

Play

Si on clique (ou touche) « PLAY » on appelle la fonction onPlay :

// Play event
onPlay: function () {
    C.sprites.title.visible = false;
    C.sprites.play.visible = false;
    C.sprites.help.visible = false;
    C.highScoreText.visible = false;
    C.sprites.bird.visible = true;
    C.messageText.visible = true;
    C.sprites.pannel.visible = true;
    C.state = C.play.on;
}

On rend visibles tous les sprites pour le jeu et on change l’état avec une fonction qu’on verra dans le prochain chapitre. On obtient maintenant cette apparence :

img01

Le jeu est en place et il ne restera plus qu’à l’animer !

Les collisions

On va avoir besoin de gérer les collisions :

  • entre l’oiseau et les avions,

  • entre l’oiseau et les ballons,

  • entre les avions entre eux pour éviter qu’ils soient empilés.

On a vu dans la première partie la librairie sat.js qu’on utilise pour cette gestion.

Voici le code complet du fichier collision.js :

/* global C, SAT */

// Aliases
var V = SAT.Vector;

C.collisions = {
    //Properties
    response: undefined,
    planePolygon: [
        new V(4, 128),
        new V(192, 8),
        new V(332, 68),
        new V(264, 201),
        new V(99, 212)
    ],
    balloonPolygon: [
        new V(10, 0),
        new V(20, 0),
        new V(20, 75),
        new V(30, 75)
    ],
    // Get plane box
    getPlaneBox: function (plane) {
        return new SAT.Box(new SAT.Vector(plane.x, plane.y), 356, 222).toPolygon();
    },
    // Get plane polygon
    getPlanePolygon: function (plane) {
        return new SAT.Polygon(new V(plane.x, plane.y), this.planePolygon);
    },
    // Get balloonn polygon
    getBalloonPolygon: function (balloon) {
        return new SAT.Polygon(new V(balloon.x, balloon.y), this.balloonPolygon);
    },
    // Get balloon circle
    getBalloonCircle: function (balloon) {
        return new SAT.Circle(new V(balloon.x, balloon.y - 35), 40);
    },
    // Get bird circle
    getBirdCircle: function () {
        return new SAT.Circle(new V(C.sprites.bird.x + 40, C.sprites.bird.y + 60), 38);
    },
    // Detect bird/plane collision
    birdPlane: function (plane) {
        return SAT.testPolygonCircle(this.getPlanePolygon(plane), this.getBirdCircle(), this.response);
    },
    // Detect bird/balloon circle collision
    birdBalloonCircle: function (balloon) {
        return SAT.pointInCircle(new V(C.sprites.bird.x + 133, C.sprites.bird.y + 83), this.getBalloonCircle(balloon), this.response);
    },
    // Detect bird/balloon polygon collision
    birdBalloonPolygon: function (balloon) {
        return SAT.pointInPolygon(new V(C.sprites.bird.x + 133, C.sprites.bird.y + 83), this.getBalloonPolygon(balloon), this.response);
    },
    // Check for free place for plane
    freeOfPlane: function (plane) {
        for (var i = 0; i < C.nbrPlanes; ++i) {
            if ((C.sprites.planes[i].state && SAT.testPolygonPolygon(this.getPlaneBox(C.sprites.planes[i]), this.getPlaneBox(plane), this.response))) {
                return false;
            }
        }
        return true;
    }
};

J’utilise un alias pour simplifier la syntaxe des vecteurs :

var V = SAT.Vector;

Les avions

Pour les avions j’ai prévu un polygone tout simple pour les collisions avec l’oiseau :

img02

Ce qui donne cette définition :

planePolygon: [
    new V(4, 128),
    new V(192, 8),
    new V(332, 68),
    new V(264, 201),
    new V(99, 212)
],

Par contre pour le positionnement initial de l’avion avant qu’il entre en scène, pour éviter que les avions soient trop proches j’utilise un rectangle :

img05

L’oiseau

Comme l’oiseau est presque circulaire j’ai défini un cercle pour lui :

img03

Ce repère est pour les collisions avec les avions, pour ce qui est des ballons on prend uniquement le bout du bec.

Les ballons

Pour les ballons j’ai distingué deux parties :

  • le ballon lui-même avec un crevaison à la clé,

  • le manche sans crevaison mais juste une inclinaison.

img04

Les fonctions de création des zones

On a des fonctions pour générer la définition des zones de collision :

// Get plane box
getPlaneBox: function (plane) {
    return new SAT.Box(new SAT.Vector(plane.x, plane.y), 356, 222).toPolygon();
},
// Get plane polygon
getPlanePolygon: function (plane) {
    return new SAT.Polygon(new V(plane.x, plane.y), this.planePolygon);
},
// Get balloonn polygon
getBalloonPolygon: function (balloon) {
    return new SAT.Polygon(new V(balloon.x, balloon.y), this.balloonPolygon);
},
// Get balloon circle
getBalloonCircle: function (balloon) {
    return new SAT.Circle(new V(balloon.x, balloon.y - 35), 40);
},
// Get bird circle
getBirdCircle: function () {
    return new SAT.Circle(new V(C.sprites.bird.x + 40, C.sprites.bird.y + 60), 38);
},

Les fonction de test de collision

On a ensuite des fonction de test de collision :

// Detect bird/plane collision
birdPlane: function (plane) {
    return SAT.testPolygonCircle(this.getPlanePolygon(plane), this.getBirdCircle(), this.response);
},
// Detect bird/balloon circle collision
birdBalloonCircle: function (balloon) {
    return SAT.pointInCircle(new V(C.sprites.bird.x + 133, C.sprites.bird.y + 83), this.getBalloonCircle(balloon), this.response);
},
// Detect bird/balloon polygon collision
birdBalloonPolygon: function (balloon) {
    return SAT.pointInPolygon(new V(C.sprites.bird.x + 133, C.sprites.bird.y + 83), this.getBalloonPolygon(balloon), this.response);
},

Espace libre d’avion

Pour terminer on a une fonction pour déterminer si un espace est libre d’avion pour positionner un avion prêt à se lancer :

// Check for free place for plane
freeOfPlane: function (plane) {
    for (var i = 0; i < C.nbrPlanes; ++i) {
        if ((C.sprites.planes[i].state && SAT.testPolygonPolygon(this.getPlaneBox(C.sprites.planes[i]), this.getPlaneBox(plane), this.response))) {
            return false;
        }
    }
    return true;
}

Si on fait le point on commence à avoir pas mal de fichiers pour notre jeu :

img06

En résumé

Dans ce chapitre on a :

  • mis en place la gestion du menu avec l’aide et le lancement du jeu

  • mis en place la gestion des collisions

Laisser un commentaire