Lingo casse des briques 

 

Le but du jeu ? Détruire les seules briques situées en haut.

 

Je ne saurais dire pourquoi mon casse-briques est si lent dans sa version Shockwave. Sous forme d'animation Director, ça tourne si vite que c'en est presque injouable ! A mon avis, c'est java qui est lent en animation.

Un chose est sûre, c'est là un excellent prétexte pour aborder le Lingo objet puisque deux balles y ont chacune un comportement autonome et pourtant similaire. Ces deux balles doivent savoir quoi faire d'elles-mêmes !

Plantons le décors pour commencer . A l'exception notable des balles en mouvement pour lesquels un seul acteur bitmap 1 bit colorisé a été utilisé deux fois , tout l'écran est composé d'acteurs de type forme. Ces acteurs sont lent en animation mais extrêmement légers et conviennent parfaitement à une construction de ce type. Les briques ne sont faites que d'un seul acteur forme rectangulaire placé 39 fois sur différentes pistes et colorisé.

Comme on le voit sur cette copie d'écran, c'est un acteur forme (ici en bleu pale mais invisible en réalité) qui est chargé de contenir les mouvement du palet (un autre acteur forme). Il suffit de placer dans un script d'initialisation l'instruction : set the constraint of sprite (numéro de sprite du palet) to (numéro de sprite de l'acteur forme invisible). Le palet dès lors ne sortira pas de la zone qu'on veut lui voir couvrir. Afin qu'il suive les mouvements de la souris, le script d'image est ainsi rédigé :

on exitFrame

set the loch of sprite (numéro de sprite du palet) to the mouseh
go to the frame

end

Le décors étant planté il nous reste à animer les balles et à gérer leurs collisions avec les murs, le palet et les briques.

Ici parce que deux balles au comportement identique sont en jeu, la programmation objet s'impose tout naturellement.

Pas question de gérer chaque balle isolément. Il faut définir un objet balle de façon abstraite. affecter à cet objet un comportement type et c'est seulement après cela que nous créerons de vraies balles, autant que nous voudrons, en leur attachant ce comportement.

Posons nous la question de savoir ce que nous voulons de chacune de ces balles.

Nous voulons d'abord que chacune se déplace librement : nous devons pour chacune être en mesure de modifier sa position.

Nous souhaitons que chacune change de sens lorsqu'elle vient heurter un mur, le palet ou une brique : l'orientation de chacune de ses balles doit donc nous être accessibles.

Nous allons définir UN MODÈLE TYPE BALLE possédant les propriétés et susceptible des comportements génériques que nous souhaitons pour toutes les vraies balles :

Les propriétés d'abord communes à chaque balle :

Toute balle aura une position (horizontale et verticale) donc deux propriétés POSH et POSV ; une orientation vers le haut et la gauche, le bas et la gauche, le haut et la droite, etc. donc un sens horizontal et vertical de déplacement : SENSH et SENSV . Chaque balle enfin devra correspondre à un sprite physiquement sur la scène sinon son existence restera très virtuelle. Toutes les balles se verront donc attaché un numéro de piste, soit une propriété NUMERODESPRITE .

Plaçons maintenant cette définition DE TOUTE BALLE à venir dans un script Lingo. Par le menu Fenêtre, commande Script nous accédons à un script d'animation vierge. Nous nommons ce script PARENTDESBALLES et nous y déclarons les propriétés communes à toutes les balles :

property POSH, POSV, SENSH, SENSV, NUMERODESPRITE

Chaque fois que nous devrons utiliser une nouvelle balle, nous choisirons un sprite et nous devrons l'indiquer à Director. Ce numéro ne sera pas toujours le même puisque nous voulons utiliser plusieurs balles : nous désignons ce numéro à venir par la variable UNSPRITE.

on new me, UNSPRITE

set NUMERODESPRITE to QUELSPRITE
set POSH to the locH of sprite NUMERODESPRITE
set POSV to the locV of sprite NUMERODESPRITE
set SENSH to 1
set SENSV to -1
return me

end

Qu'est-ce que cela veut dire ? Rien d'autre que ceci : toutes les balles auront une propriété position qui, pour commencer, sera la positon du sprite associé sur la scène. Toutes les balles se reconnaîtront dans la variable "me".

Maintenant toutes les balles devront se mouvoir librement, nous allons définir un comportement type pour toutes les balles que nous pourrions être amené à utiliser :

Placé sur le même script un gestionnaire on stepFrame va nous permettent d'instruire ce que toute balle devra faire à chaque fois que la tête de lecture reviendra sur l'image. Décidons que toutes les balles, sauf à sortir de la zone de jeu, devront voir leur position modifiée de deux pixels. Bien sûr le sprite associé sur la scène devra se mouvoir aussi :

on stepFrame me

rebonditContreLesMursEtLePalet me

set POSH to POSH + 2 * SENSH
set POSV to POSV + 2 * SENSV
set the loc of sprite NUMERODESPRITE to point( POSH, POSV)

end

Dans la variable "me", toute les balles se reconnaîtront et exécuteront les instructions.

Évidemment, l'instructions rebonditContreLesMursEtlePalet n'a aucun sens pour Lingo. Il nous faut la définir. cette instruction de comportement aura un argument : à chaque fois une des balles créées : par convention "me".

on rebonditContreLesMursEtlePalet me

case true of

( POSH >= the right of sprite LA_SURFACE_DE_JEU ) :

set SENSH to SENSH * -1

set POSH to POSH + 4 * SENSH

 

( POSH <= the left of sprite LA_SURFACE_DE_JEU ) :

set SENSH to SENSH * -1

set POSH to POSH + 4 * SENSH

 

( POSV <= the top of sprite LA_SURFACE_DE_JEU ) :

set SENSV to SENSV * -1

set POSV to POSV + 4 * SENSV

 

( POSV >= the bottom of sprite LA_SURFACE_DE_JEU ) :

set SENSV to SENSV * -1

set POSV to POSV + 4 * SENSV

 

( sprite pNumeroDeSprite intersects
sprite LE_PALET ) :

set SENSV to SENSV * -1

set POSV to POSV + 4 * SENSV

end case

Ce comportement indique à toutes les balles de modifier leurs propriétés SENSH et SENV à chaque fois qu'une balle heurte un mur ou le palet. Nous avons également pris la précaution de repousser vivement (4 pixels) les balles en sens inverse, vers l'intérieur de la zone de jeu donc pour éviter qu'une fois le mur franchi, l'instruction qui inverse le sens ne soit exécutée infiniment.

À ce stade de notre travail, nous pouvons déjà effectuer un test :

Créons deux balles réelles en attachant à deux sprites (40 et 41) placés sur la scène le comportement que nous avons défini.

Dans la fenêtre message nous saisissons :

set UNEBALLE to new(script "PARENTDESBALLES", 40)

set UNEAUTREBALLE to new(script "PARENTDESBALLES", 41)

Immédiatement les balles se meuvent dans l'espace de jeu et rebondissent contre les murs. Bon début ! Essayez dans Director, vous verrez comme ça va vite.

Il nous reste quelques améliorations : d'abord la gestion des collisions avec les briques car pour l'instant nos balles se moquent de ces obstacles; le lancement du jeu par l'utilisateur (qui ne va pas utiliser la fenêtre message lui !) et enfin, pour que le jeu prenne sens, la vérification du score en fin de partie.

Pour gérer les collisions avec les briques, il nous faut reprendre le script que nous avons nommé PARENTDESBALLES, pour ajouter une instruction supplémentaire au gestionnaire on stepFrame  :

on stepFrame me

rebonditContreLesMursEtLePalet me
gereCollisionsAvecBriques me

set POSH to POSH + 2 * SENSH
set POSV to POSV + 2 * SENSV
set the loc of sprite NUMERODESPRITE to point(POSH, POSV)

end

L'instruction gereCollisionsAvecBriques est détaillée ensuite comme suit (toujours dans la même fenêtre de script) :

on gereCollisionsAvecBriques me

global CIBLES

-- On utilise une liste des 39 sprites briques,
-- liste créée dans un script d'initialisation

repeat with n in CIBLES

-- Pour chacune des 39 briques ("n") on vérifie :

if sprite NUMERODESPRITE intersects sprite n then

-- auquel cas on renvoie la balle
-- et on rend la brique invisible

set SENSV to SENSV* -1

set the visible of sprite n to false

set POSV to POSV + 4 * SENSV

-- Il convient d'ôter cette brique
-- invisible de la liste des cibles
-- et d'arrêter le test :

deleteOne CIBLES, n
exit repeat

end if

end repeat

-- maintenant on veille à ce que le
-- jeu prenne fin quand il n'y a plus
-- de cibles :

-- admettons que l'on ait placé les briques
-- à détruire sur les pistes 28 à 39; s'il ne
-- reste plus qu'une de celles-là alors c'est
-- perdu puisqu'il n'y a plus de briques violettes.


if getAt(CIBLES, 1) >= 28 then arretePerdu

-- s'il ne reste plus que des briques violettes
-- (placées sur les pistes jusque 28), alors
-- c'est gagné

if getLast (CIBLES) < 28 then arreteGagne

end

Nous vous laissons le soin de rédiger ces deux gestionnaires arretePerdu et arreteGagne. Apparition du score dans un champs, marquage sonore... mais il nous semble important de mentionner la NÉCESSITÉ d'y inclure les instructions détruisant les objets créés (les balles) et libérant la mémoire :

on arreteGagne

global UNEBALLE, UNEAUTREBALLE
set the text of member x to count(CIBLES)
set UNEBALLE to 0
set UNEAUTREBALLE to 0

end


bonne chance.