2012-10-03 12 views
5

Sto usando Box2D per la prima volta seriamente in un gioco Flash di medie dimensioni su cui sto lavorando. La mia attuale esperienza con Box2D è limitata alla creazione di un mondo, corpi e l'aggiunta di quei corpi al mondo in modo funzionale.Come posso tenere traccia di tutte le collisioni di Box2D in modo pulito e gestibile?

Sto trovando abbastanza facile integrare Box2D nel mio ambiente di gioco, mantenendo codice ben scritto e completato alcuni tutorial che gestiscono le collisioni. Il problema che sto affrontando ora è che il mio gioco avrà molti corpi, ognuno interagendo con altri corpi in modi diversi, e sto trovando difficile scrivere la mia sottoclasse b2ContactListener senza che questo diventi estremamente disordinato.

Sulla base di un tutorial che ho utilizzato, ho creato la mia sottoclasse di b2ContactListener e aggiunto un override del metodo BeginContact(). L'argomento che BeginContact() riceve quando viene chiamato farà riferimento a un'istanza di b2Contact, attraverso la quale posso accedere a due istanze b2Fixture (le due istanze che sono entrate in conflitto). Sono quindi in grado di accedere all'istanza b2Body associata a ciascuno di questi b2Fixture s.

  • Problema: Attualmente ho un modo indiretto di scoprire che cosa si sono scontrati due cose (cioè se sono un muro e un missile, o il giocatore e un albero, ecc) che utilizza GetUserData() e sguardi come questo come esempio:

    var f1Player:Boolean = contact.GetFixtureA().GetBody().GetUserData() is Player 
    var f2Player:Boolean = contact.GetFixtureB().GetBody().GetUserData() is Player 
    var f1Tree:Boolean = contact.GetFixtureA().GetBody().GetUserData() is Tree 
    var f2Tree:Boolean = contact.GetFixtureB().GetBody().GetUserData() is Tree 
    // ... continutes with all possible combinations. 
    
    
    // Example of managing a collision: 
    if(f1Player && f2Tree) 
    { 
        // Player (FixtureA) and Tree (FixtureB) 
    } 
    
    if(f2Player && f1Tree) 
    { 
        // Player (FixtureB) and Tree (FixtureA) 
    } 
    

    come si può vedere, questo sta per finire estremamente lungo e ingestibile. Devo anche scrivere ogni serie di azioni da eseguire due volte per far fronte a un determinato elemento che è FixtureA o FixtureB, o viceversa (ovviamente sotto forma di una chiamata di funzione con i parametri scambiati piuttosto che letteralmente riscritta).

Questo non è chiaramente l'approccio corretto, ma non sono stato in grado di individuare le risorse che spiegano in modo più approfondito la gestione di rilevamento delle collisioni.

Qualcuno ha esperienza con la gestione del rilevamento delle collisioni utilizzando Box2D che possono condividere? Inoltre, sta usando SetUserData(entityThatOwnsTheBody); il modo corretto di usare quel metodo?

risposta

1

Ho inventato qualcosa di molto più bello dell'originale.

In primo luogo, ho solo la mia classe Being (che possiede uno b2Body) impostato come "UserData". Questa classe conterrà anche un metodo onContact() e aspetto simile a quello qui sotto:

public class Being 
{ 

    private var _body:b2Body; 


    public function Being() 
    { 
     // Define the body here. 
     // ... 

     _body.SetUserData(this); 
    } 


    public function onCollision(being:Being = null):void 
    { 
     // 
    } 

} 

Poi nel mio b2ContactListener implementazione, ho semplicemente passare il collisione Being (o nullo, se non c'è Being assegnato alla collisione b2Body ' s UserData) per s' onCollision() la opposte Being:

override public function BeginContact(contact:b2Contact):void 
{ 
    var bodyA:b2Body = contact.GetFixtureA().GetBody(); 
    var bodyB:b2Body = contact.GetFixtureB().GetBody(); 

    var beingA:Being = bodyA.GetUserData() as Being || null; 
    var beingB:Being = bodyB.GetUserData() as Being || null; 

    beingA && beingA.onCollision(beingB); 
    beingB && beingB.onCollision(beingA); 
} 

E infine in ciascuno dei miei sottoclassi di Being, posso facilmente preparare la logica appropriata per un collisi tra altri Being s di un certo tipo:

class Zombie extends Being 
{ 
    override public function onCollision(being:Being = null):void 
    { 
     if(being && being is Bullet) 
     { 
      // Damage this Zombie and remove the bullet. 
      // ... 
     } 
    } 
} 
+1

Sembra che la lunga dichiarazione "se" venga appena trasferita nella classe Being. Che ne dici di un'elaborazione che dovrebbe essere eseguita una sola volta per la collisione, ad es. suonare un suono o incrementare un punteggio. – iforce2d

+0

@ iforce2d È vero, voglio dire che ci saranno le dichiarazioni 'if()' in ogni caso per aggiungere criteri per ciascuna combinazione di collisioni, ma questo sembra più organizzato di avere tutto in un unico file. Mi fornisce la possibilità di localizzare facilmente una collisione tra due cose tramite le classi rilevanti e non devo controllare quale sia l'Essere che per ogni collisione manualmente. – Marty

+0

Come per le singole occorrenze, che possono essere gestite anche dall'individuo con un semplice 'hasBeenTouched' booleano o simile. – Marty

1

Ho usato la versione c++ di Box2d, ma penso che lo stesso approccio funzionerà con ActionScript. Creo una classe Object, che contiene un puntatore b2Body *_body e un puntatore alla rappresentazione grafica. _body's UserData è stato impostato su Object *. Classe Object aveva i seguenti metodi:

virtual bool acceptsContacts (); 
virtual void onContactBegin  (const ContactData &data); 
virtual void onContactEnded  (const ContactData &data); 
virtual void onContactPreSolve (const ContactData &data); 
virtual void onContactPostSolve (const ContactData &data); 

Quando collisione è stata rilevata nel b2ContactListener sottoclasse, è controllato se i corpi in collisione sono i dati degli utenti. In tal caso, ha inoltrato i propri dati utente a Object* e, se uno qualsiasi degli oggetti collisi ha accettato i contatti, ha creato ContactData (una classe con tutte le informazioni necessarie sulla collisione) e lo ha inserito nella sua list interna da consegnare successivamente.

Quando viene restituito il metodo b2World::update, ContactListener recapita tutte le informazioni di contatto agli oggetti da elaborare.Consegna è stata ritardata al fine è possibile creare nuovi corpi, articolazioni e così via, proprio durante l'elaborazione di collisione (che non è consentita durante l'aggiornamento è in esecuzione)

Inoltre si deve notificare ContactListener (basta mettere un puntatore ad esso all'interno ContactData) se uno dei corpi in collisione è stato eliminato durante l'elaborazione della collisione, quindi può invalidare i contatti appropriati e non consegnarli

2

Sì, è davvero un po 'fastidioso. In realtà penso che il modo in cui lo hai è abbastanza tipico.

fww ​​Lo stesso Box2D deve affrontare un problema simile quando si verifica se i dispositivi si sovrappongono. Ci sono un sacco di funzioni come b2CollideCircles, b2CollidePolygonAndCircle, b2CollidePolygons ecc. E quando due proiettori si avvicinano l'un l'altro il motore sceglie quale di queste funzioni dovrebbe essere usata.

Fa ciò mettendo i puntatori di funzione in una matrice bidimensionale, quindi cerca la funzione appropriata in questa matrice utilizzando i due tipi di forma come indice. Vedi le prime tre funzioni in b2Contact.cpp per i dettagli.

Ovviamente, se non si riescono a trasferire riferimenti di funzioni come questo in AS3, suppongo che questa risposta non sia di grande aiuto, ma ho pensato di postare comunque come gli utenti C/C++/JS potrebbero venire.