2 – Les astéroïdes
Pour l'objet « Asteroid », c'est un sprite avec une texture qui provient du pack gratuit. Vous pouvez sélectionner l'instance au coin en haut à gauche du « layout » dans Layout view.
« Asteroid » passe d'un côté de l'écran à l'autre comme le vaisseau (comportement Wrap), il bouge automatiquement (comportement Bullet) et il tourne sur lui même (comportement Rotate).
Dans les propriétés d'« Asteroid », la propriété « set angle » (définir l'angle) est sur « No ». Comme cela, le comportement bullet fera en sorte que l'astéroïde bouge dans l'écran en ligne droite, et le comportement « rotate » ira définir l'angle d'affichage de la texture de l'astéroïde, rendant ainsi l'illusion que l'astéroïde tourne sur lui même pendant qu'il suit une trajectoire en ligne droite.
Puis à chaque fois que l'astéroïde quittera le terrain de jeu, le comportement « wrap » ira automatiquement le faire apparaître sur l'autre côté de l'écran.
Une autre idée est que quand « l'on tire sur un astéroïde, il se casse en deux astéroïdes plus petit ». « Astéroid » a une variable d'instance qui influe sur la taille, qui vérifie que quand l'astéroïde est touché, soit il se casse en deux plus petits morceaux, soit disparaît tout simplement.
Cette variable d'instance se nomme « size » (taille) et est une variable utilisant un nombre.
Le nombre par défaut est 1.
+ Décomposons le code
Dans « esGame », le groupe "AsteroidHandling" (event 16) contient le code contenant la mécanique du jeu.
Tout se passe au moment de la collision entre « Asteroid » et la « Bullet ».
La première action faite est de détruire la « Bullet » au cas où l'événement serait executé pendant le prochain « tick » (temps de rafraîchissement de la mémoire de l'ordinateur, on parle souvent de « tick/frame per seconde » (fps), celui-ci est souvent de 60 ticks par secondes – le temps de rafraîchissement d'un écran), pour prévenir que la « Bullet » frappe d'autres « Asteroids ».
L'Event 18 est vide (il n'a pas de condition), il sera donc exécuté chaque tick.
Comme si c'était un sous-événement de l'event 17, il ne s’exécute que si l'événement 17 s’exécute.
Et l'événement 17 est une triggered condition (condition déclenchée) (dans la section "Events run top to bottom" de l'article en lien). Ce sous-événement s'exécute pour un tick chaque fois qu'elle est déclenchée.
Cela permet de créer une variable local et stocker une valeur dedans.
Utiliser une variable locale nous assure que ces valeurs seront remises à zéro chaque tick et qu'elle ne peut être modifiée ou utilisée en dehors de cet événement.
Donc une fois que la collision est détectée, la première étape et de mettre le UID de l'« asteroid » dans une variable locale. (event 18)
Cela va permettre d'encore utiliser l'astéroïde à la fin du processus qui le détruit.
C'est une anticipation du fait que nous pourrions créer deux nouvelles instances d'« Asteroid » et C2 utilise toujours le dernier objet apparu.
L'angle du déplacement de l'instance de l'astéroïde est aussi enregistré dans une variable locale.
Cela sera utilisé plus tard quand un nouvel astéroïde apparaîtra comme trajectoire de base qui fera diverger l'instance de son angle d'origine.
Event 19 is a sub event to event 17 and is "paired" with event 23.
This event is a test on the current picked instance (the "Asteroid" instance that has collided with a "Bullet") to see if its "Size"'s value is less than 2.5.
If it is, this means that we will split the asteroid into two smaller ones.
System: Set CurrentSize to Asteroid.Size + 0.5
This action stores the current "Size" (instance variable) value and adds 0.5 to it.
This is for the splitting of asteroids which is explained a few lines below.
Adds 1 to the global variable "Score", the player just scored 1 point because he hit a "split-able" "Asteroid" with one of his "Bullet".
The splitting into two new instances happens event 20 which is a "Repeat 2 times" sub event.
It will only execute if it's parent event is executed (event 19, so when Asteroid.Size < 2.5) and will repeat the bunch of actions two times.
System: Create Asteroid at Asteroid.X, Asteroid.Y on layer 0
Edit: In the original capx and screenshots, the action was a "Asteroid: Spawn Asteroid", but stable release r103.2 (or earlier) broke it, so I have had to switch the action there to keep the game working as intended. The capx has been updated too.
The system creates a new "Asteroid" at the position of the currently picked "Asteroid" instance.
The first time the event runs, the picked instance is the instance that was in collision with "Bullet", the second time it is this newly spawned instance.
And actually, from this action, this is also the picked instance.
All the following actions will apply to this newly spawned instance and only this one.
Asteroid: Set Bullet angle of motion to int((CurrentAOM + random(125))%180) degrees
This action contains a bit of maths and a bit of system expressions. (the expressions each have a definition, be sure to check the manual out whenever you're wondering about them)
Let's break it down:
Is a system expression that allows to make sure that the content between the parenthesis will be an integer.
Is a local variable we set earlier that contains the Bullet angle of motion of the parent "Asteroid" instance.
Is another system expression that will return (generate) a random number.
In this case it will generate a number between 0 and 124 (included) to add to the parent's angle of motion, and make sure the child's trajectory is different from its parent's.
The value 125 was achieved arbitrary and through trials and errors/tweaking. It seemed to achieve the effect I was visualising, so it's good enough for me.
Is the mathematical "modulo" (or "modulus" ?) that makes sure the result between the parenthesis can't be greater than 180.
The modulo is necessary here because the bullet angle of motion can be expressed in the range of -180 to 180.
Asteroid: Set size (width, height) to ( 96 / CurrentSize , 96 / CurrentSize )
Affects the width and height properties of the Sprite object type and sets the current size of the texture/object to 96 (an arbitrary start value) divided by the value of the local variable CurrentSize which is the "Size" (instance variable) value of the parent instance + 0.5.
Dividing a value by a greater value returns a diminished result. So the newly spawned "Asteroid" is CurentSize smaller than its parent.
Asteroid: Set "Size" (instance variable) value to CurrentSize
Sets the current instance's "Size" value to the value of the local variable CurrentSize.
As on each split the "Size" value is incremented, the "Asteroid"'s size gets smaller, and it allows for the event19/event23 pairing/logic.
Asteroid: Set Rotate speed to random(20,180) degrees per second
As earlier, the random() system expression returns a float number here (because there is no int() in the formula) between 20 and 179 (included).
It sets the rotation speed (on itself, Rotate behavior) for the current "Asteroid" instance, allowing for a bit of visual diversity on screen (not all asteroids rotates at the same speed).
Asteroid: Set Bullet speed to random(AsteroidMaxSpeed - 10,AsteroidMaxSpeed)
Notice "AsteroidMaxSpeed" is a global variable I've set up while tweaking to get the nicest global speed for the Asteroids in my opinion.
The speed is randomized but kept in a close range so the Asteroids will generally move at the same speed (modulated by 10 pixels per seconds).
As one of the aim here is to produce a full project in less than 100 events, if you were to need space for one more event, you could put solid values (40,50) in this action and delete the global variable.
I kept it in here to show this as a tip for when you are tweaking. It is faster to edit only this one value and hit "Preview".
Also sometime, if the value is to be used across several spots of the event sheet/project, it is a viable solution to keep the value in a single spot and refer to it through its variable name in the sheets.
The two last actions are here for a visual final touch.
Asteroid: Set angle to Asteroid.Bullet.AngleOfMotion degrees
Asteroid: Move forward random(5,15) pixels
The newly spawned "Asteroid" is moved away by a few pixels from its parent.
For it to work, for this one tick, the angle of the Sprite is set to its Bullet angle of motion.
Then the "Asteroid" is "forwarded" by a random amount of pixels (at least 5, at max 14).
This is it for the split.
System: Pick all Asteroid
And so now, we need to "reset the picking" thanks to the system condition "Pick all".
At this point of the code, the instance picked is the second "Asteroid" child instance we spawned. That's why there's the need to "Pick all (instances" here.
Picking all "Asteroids" tells C2 that we want to select another instance among all of the "Asteroid" instances available.
System: CurrentUID not = 0
CurrentUID being a local variable, each tick its value is reset to 0.
It is unlikely that an "Asteroid" instance has the UID 0 (this UID is reserved to the very first object that was created in the project, and it wasn't an "Asteroid").
This check is not really useful, but it prevent execution if an incorrect UID (the local variable default value: 0) is stored. It's only a check, it's not worth much, and it could prevent deleting the wrong instance.
Asteroid: Pick instance with UID CurrentUID
Finally this common condition picks the parent "Asteroid" instance by its UID, which we had kept at the beginning of this process in the local variable CurrentUID. (the blank event 18)
The action "Asteroid: destroy" will apply to this instance, and this instance only.
I won't talk about the sub event 22 for now, it is part of the Audio system explained later in this tutorial.
Event 23 is a "Else" condition that will occur when event 19 (a test to see if the size of the "Asteroid" in collision is less than 2.5) is not executed (because its condition is not true, the "Asteroid"'s size is 3).
If this event executes, it means that the "Asteroid" instance in collision with a "Bullet" "Size" instance variable value is equal to 3.
In the game logic, and because that's the value I arbitrary chose via tweaking, it means it is the last piece, it won't split, just be destroyed.
It also adds 10 points to the player's score.
As earlier, the sub event 24 is for the audio and discarded in the discussion for now.
This closes the "AsteroidHandling" group
Remember the game point list in the first pages of the tutorial ?
Asteroid clone - base Mechanism
..° Ship moving like in the original Asteroid game (gravity 0-like type of physics, the ship rotates on itself, it can go forward, brake, momentum is present, wraps from one side of the screen to the other; it also can shoot at asteroids), controlled by the player.
..° Asteroids are moving rocks on a straight trajectory, warping on the sides of the screen like the player's ship, each asteroid hit by a bullet splits into two smaller asteroids, or if it is already the "smallest" allowed, is destroyed. Splitting an asteroid score 1 point, destroying an asteroid scores 10.
..° If the ship collides with an asteroid, the ship loses health up to 0 where it is game over. The asteroid is just destroyed, no score added.
The last point is handled in the group "PlayerHandling" (event 12)
Event 13 the "Player" collides with an "Asteroid".
Player: Substract int(35/ Asteroid.Size) from Health
This action does reduce the "Health" instance variable value according to the "Asteroid"'s "Size" value.
If the "Player" collides with a big "Asteroid" ("Size" = 1) it will lose 35 health points. (35/1 = 35)
If the "Player" collides with a small "Asteroid" ("Size" = 3) it will lose around 11 health points. (35/3 = 11.66...)
The "Health" is then displayed by the "LifeBar", but this matter will be discussed later in the tutorial.
The "Asteroid" is simply destroyed.
Event 15, the "Player"'s "Health" is equal or less than 0.
This is game over.
For now, all you need to know about it is that it sets all the values required in the game logic, and goes to the "Score" layout.
More on this later.
With this group "PlayerHandling" you can see that the last basic mechanic of game-play is implemented.
This closes the presentation of the base mechanism elements, the game element the players can interact with.