Starling API – 29 – Réagir à la collision entre 2 body Box2D
Pour l'instant, on peut avec notre poisson, détruire le décor composé de body Box2D associés à des MovieClip que l'on a créé dans un tutorial précédent:
Starling API - 17 - Création des éléments du décor avec BlockFactory, TerrainFactory et EnemyFactory
Nos blocs sont donc des MovieClip contenant plusieurs Texture. Les Textures sont en fait les mêmes mais avec une opacité différente. Si vous avez un graphiste, vous auriez des dessins différents pour chaque état de destruction du bloc. Pour notre test en tout cas, tout bloc qui sera touché va devenir de plus en plus transparent suivant la force qui lui sera appliquée.
Détecter une collision avec b2World::SetContactListener()
Il y a une méthode simple pour détecter les collisions qui est de placer un "ContactListener" sur le monde (objet de type b2World). Ce ContactListener est une classe qui étend b2ContactListener. Créons donc la classe BlockContactListener:
package com.fnicollet {
import Box2D.Dynamics.b2ContactListener;
public class BlockContactListener extends b2ContactListener {
public function BlockContactListener() {
super();
}
}
}
Dans la méthode init() de AquariumScreen, on l'associé à notre monde:
world.SetContactListener(new BlockContactListener);
Ensuite, dans BlockContactListener, il faut surcharger la méthode suivante:
function PostSolve(contact:b2Contact, impulse:b2ContactImpulse):void
Cette méthode nous donne accès à un objet b2Contact qui a des références vers les 2 objets en contact et à un objet b2ContactImpulse qui contient des informations sur la collision comme sa force. Pour récupérer la force de la collision, on va récupérer la valeur de la normale:
override public function PostSolve(contact:b2Contact, impulse:b2ContactImpulse):void {
var firstNormalImpulse:Number = impulse.normalImpulses[0];
}
Si cette valeur est supérieure à un certain seul, on va récupérer les 2 objets en contact:
override public function PostSolve(contact:b2Contact, impulse:b2ContactImpulse):void {
const MAX_IMPULSE:Number = 10;
var firstNormalImpulse:Number = impulse.normalImpulses[0];
if (firstNormalImpulse > MAX_IMPULSE) {
var userDataA:DisplayObject = contact.GetFixtureA().GetBody().GetUserData();
var bodyDefinitionA:b2BodyDef = contact.GetFixtureA().GetBody().GetDefinition();
var userDataB:DisplayObject = contact.GetFixtureB().GetBody().GetUserData();
var bodyDefinitionB:b2BodyDef = contact.GetFixtureB().GetBody().GetDefinition();
var nameA:String = userDataA.name;
var nameB:String = userDataB.name;
}
}
Ensuite, on va détecter la collision entre un fish (dont les noms commencent par "bird_") et un corps Box2D dynamique (nos blocks). En même temps, on va faire pareil pour les collisions avec le sol. Le code n'est pas super mais c'est pour vous expliquer en gros:
override public function PostSolve(contact:b2Contact, impulse:b2ContactImpulse):void {
const MAX_IMPULSE:Number = 10;
var firstNormalImpulse:Number = impulse.normalImpulses[0];
if (firstNormalImpulse > MAX_IMPULSE) {
var userDataA:DisplayObject = contact.GetFixtureA().GetBody().GetUserData();
var bodyDefinitionA:b2BodyDef = contact.GetFixtureA().GetBody().GetDefinition();
var userDataB:DisplayObject = contact.GetFixtureB().GetBody().GetUserData();
var bodyDefinitionB:b2BodyDef = contact.GetFixtureB().GetBody().GetDefinition();
var nameA:String = userDataA.name;
var nameB:String = userDataB.name;
var densityA:Number = contact.GetFixtureA().GetDensity();
var densityB:Number = contact.GetFixtureB().GetDensity();
var isAFloor:Boolean = densityA == 0;
var isBFloor:Boolean = densityB == 0;
var isAFish:Boolean = nameA && nameA.indexOf("GUPPY_") != -1;
var isBFish:Boolean = nameB && nameB.indexOf("GUPPY_") != -1;
if ((isAFloor || isAFish) && bodyDefinitionB.type == b2Body.b2_dynamicBody) {
destroyDisplayObjectBy(userDataB, firstNormalImpulse);
}
if ((isBFloor || isBFish) && bodyDefinitionA.type == b2Body.b2_dynamicBody) {
destroyDisplayObjectBy(userDataA, firstNormalImpulse);
}
}
}
private function destroyDisplayObjectBy(dObj:DisplayObject, damage:Number):void {
if (damage < 1) {
return;
}
var mc:MovieClip = dObj as MovieClip;
if (!mc) {
return;
}
if (mc.currentFrame == mc.numFrames - 1) {
mc.removeFromParent();
return;
}
mc.currentFrame++;
}
Faire le ménage avant de partir
Si vous testez l'application comme cela, vous verrez que lorsque nos blocs ont pris beaucoup de dégâts, ils disparaissent. Mais il sont encore pris en compte dans le calcul des collisions ! En effet, on a supprimé le MovieClip, donc l'affichage graphique. Le "body" Box2D est lui toujours présent et pris en compte dans la simulation.
On va donc rajouter le "body" à notre méthode destroyDisplayObjectBy et supprimer l'objet du monde pour de bon:
private function destroyDisplayObjectBy(dObj:DisplayObject, damage:Number, body:b2Body):void {
if (damage < 1) {
return;
}
var mc:MovieClip = dObj as MovieClip;
if (!mc) {
return;
}
if (mc.currentFrame == mc.numFrames - 1) {
mc.removeFromParent();
body.GetWorld().DestroyBody(body);
return;
}
mc.currentFrame++;
}
Si vous essayez, vous verrez que cela ne fonctionne pas. En cherchant un peu sur le net, on s'aperçoit que la suppression d'un body pendant la simulation n'est pas la bonne solution. Il faut garder une liste des éléments à supprimer et le faire avant le prochain rendu (=Step).
On rajoute donc une variable static à BlockContactListener pour faire simple:
public static var toRemove:Array = [];
puis:
if (mc.currentFrame == mc.numFrames - 1) {
mc.removeFromParent();
if (toRemove.indexOf(body) == -1){
toRemove.push(body);
}
return;
}
Et dans notre méthode updateWorld, on supprime les éléments en attente:
private function updateWorld(event:Event):void {
var toRemove:Array = BlockContactListener.toRemove;
for each (var bodyToRemove:b2Body in toRemove) {
world.DestroyBody(bodyToRemove);
}
BlockContactListener.toRemove = [];
Voilà le rendu final:
Conclusion
Et voilà, le fil rouge des tutoriaux Starling touche à sa fin. Dans le prochain article (et le dernier), on verra les pistes d'amélioration qu'il vous reste si vous souhaitez continuer
.
Articles similaires
- Starling API – 12 – Détecter la collision entre 2 MovieClip
- Starling API – 28 – Détecter l'arrêt d'un body Box2D
- Starling API – 5 – La classe Juggler et l'interface IAnimatable
- Starling API – 10 – Afficher des bonus de manière aléatoire
- Starling API – 21 – Lier Box2D avec les objets graphiques Starling
Aucun trackbacks pour l'instant





