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 )
:
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