2010-07-22 6 views
6

Ho questo funzionamento ok (ish) nel mio gioco al momento, ma non sono fantastico in matematica. Quando due primitivi si scontrano, voglio che si trasformino in minuscoli bit se la forza applicata a un primativo era superiore a una soglia impostata. Il mio gestore di eventi di collisione al momento assomiglia a questo.Problemi del gestore di eventi OnCollision in C# XNA con Farseer Physics

public bool Collision(Fixture fixtureA, Fixture fixtureB, Manifold manifold) 
{ 
    Vector2 position = manifold.LocalNormal; 
    float angle = (float)Math.Atan2(position.Y, position.X); 
    Vector2 force = Vector2.Zero; 
    if (angle < 0) 
    force = new Vector2((float)(Math.Cos(angle) * fixtureA.Body.LinearVelocity.X), (float)Math.Sin(MathHelper.TwoPi + angle) * fixtureA.Body.LinearVelocity.Y); 
    else 
    force = new Vector2((float)(Math.Cos(angle) * fixtureA.Body.LinearVelocity.X), (float)Math.Sin(MathHelper.TwoPi - angle) * fixtureA.Body.LinearVelocity.Y); 
    double XForce = Math.Sqrt(force.X * force.X); 
    double YForce = Math.Sqrt(force.Y * force.Y); 
    double totalForce = XForce + YForce; 
    if ((Breakable) && (totalForce > BreakForce)) 
    { 
     Breakable = false; 
     Active = false; 
     BreakUp(fixtureA, fixtureB); 
    } 
    return true; 
} 

L'ho messo tanto tempo fa, mentre stavo solo giocando. Ciò causa un po 'di problemi in determinate situazioni. Per esempio, se un primitivo è fermo sul pavimento e un altro primitivo cade su di esso da un'altezza decente, quasi sempre, la scatola che cade esplode e la scatola di riposo sopravvive. Anche se due scatole stanno cadendo fianco a fianco e si danno il più piccolo dei tocchi, allora entrambe le scatole esploderanno a mezz'aria. Hmmmmm, non proprio perfetto. Qualcuno ha qualche idea su come migliorare il mio gestore di collisioni? Grazie in anticipo.

risposta

6

(se questo è attualmente la risposta accettata - mi rivolgo a chiunque di mia other answer per un approccio potenzialmente superiore.)

il calcolo della forza di impatto è completamente sbagliato. Hai bisogno di ottenere la velocità relativa al punto (i) di contatto: stai ottenendo qualcosa di piuttosto strano ...

Il tuo codice sembra che stia usando Farseer 3.0 (dovresti specificare, perché è più un fork di Box2DX di Veggente 2.1). Quello che ho fatto a dei Lungavista 2.1 (dove hai ContactList contacts invece di un Manifold) per ottenere la velocità d'impatto è stato:

foreach(Contact contact in contacts) 
{ 
    Vector2 position = contact.Position; 
    Vector2 v0; 
    me.Body.GetVelocityAtWorldPoint(ref position, out v0); 
    Vector2 v1 = new Vector2(); 
    if(!hit.Body.IsStatic) 
     hit.Body.GetVelocityAtWorldPoint(ref position, out v1); 
    v0 -= v1; 

    float hitVelocity = v0.Length(); 
    // To then get the force, you need the mass of the two objects 
} 

Da un breve sguardo alla fonte dei Lungavista 3.0, sembra che Manifold ha un membro:

public FixedArray2<ManifoldPoint> Points; 

ed entrambi Manifold e ManifoldPoint hanno membri:

public Vector2 LocalPoint; 

dovrebbe essere abbastanza simpl e per modificare il mio codice Farser 2.1 per utilizzarli.

Inoltre: si consiglia di contrassegnare semplicemente i due oggetti come bisognosi di interruzione e quindi di rompere li dopo il l'aggiornamento della fisica termina in esecuzione (anziché nel gestore di collisioni).

+0

Ciao Andrew, Grazie per la risposta. La mia funzione BreakUp() aggiunge semplicemente un carico di nuovi primati in una lista basata sulla posizione del primitivo di rottura (i chip se vuoi) che non vengono aggiunti fino alla fisica successiva "Step()". Allo stesso modo una scatola distrutta viene semplicemente contrassegnata per essere rimossa nel nextstep. Quindi nel tuo esempio ... userei LocalPoint piuttosto che la variabile di posizione nella tua funzione corretta? Inoltre posso vedere che crea una variabile hitVelocity ma non sono sicuro di come questo si riferisca alla forza applicata all'oggetto. Conosco F = MA ma gli angoli non entrano in gioco qui? – DrLazer

+1

Sì, utilizzare LocalPoint. La mia variabile di posizione è nello spazio mondiale (quindi trova l'equivalente di spazio locale di GetVelocityAtWorldPoint). Per trovare la forza è necessario l'accelerazione (F = MA). Puoi approssimare l'accelerazione determinando quanto cambierà la velocità in quel fotogramma (differenza di velocità divisa per il tempo). –

+0

Vedi anche: http://en.wikipedia.org/wiki/Impulse_(physics) –

2

Non ho usato XNA, ma come problema di fisica, perché non solo sottrarre la velocità lineare di A dalla velocità lineare di B, e ottenere la 'forza' come il quadrato del vettore risultante (sommare i quadrati dei componenti)? Questo dovrebbe armonizzarsi con l'energia cinetica implicata secondo `E = (mv^2)/2 ', per un modello fisico molto semplice, anche se ignoriamo le masse (o, per questo, l'elasticità o la distinzione tra energia e impulso). Se gli oggetti si muovono nella stessa direzione generale alla stessa velocità, si ottiene un valore basso; se ci si avvicina (o, naturalmente, si parte!) ad alta velocità, si ottiene un valore elevato.

+2

Questo non funzionerebbe per, ad esempio, un raggio rotante lungo che "schiaffeggia" un altro oggetto. –

+0

@Andrew: corretto, non lo farebbe. Solo un po 'più realistico del codice postato ... –

8

Ok, quindi il mio other answer è valido. Ma ho guardato più attentamente Farseer 3.0 (l'attuale versione SVN) e ho scoperto che lo implementa già quasi esattamente quello che stai cercando di fare.

Cercare "BreakableBody.cs". Potresti essere in grado di utilizzarlo direttamente, ma altrimenti potresti semplicemente copiare la funzionalità che desideri.

In particolare: invece di collegare una funzione al telefono OnCollision del dispositivo, si desidera allegarne uno a PostSolve. Ci vuole uno ContactConstraint che puoi immergerti e trovare gli impulsi dalla collisione.

Questa è l'implementazione di tale funzione utilizzata da BreakableBody:

private void PostSolve(ContactConstraint contactConstraint) 
{ 
    if (!Broken) 
    { 
     float maxImpulse = 0.0f; 
     for (int i = 0; i < contactConstraint.manifold.PointCount; ++i) 
     { 
      maxImpulse = Math.Max(maxImpulse, 
         contactConstraint.manifold.Points[0].NormalImpulse); 
      maxImpulse = Math.Max(maxImpulse, 
         contactConstraint.manifold.Points[1].NormalImpulse); 
     } 

     if (maxImpulse > Strength) 
     { 
      // Flag the body for breaking. 
      _break = true; 
     } 
    } 
} 

Apparently i dati impulso nei collettori è impostato correttamente solo in PostSolve, non in OnCollision.

+0

Mr Russell you sono un vero gentiluomo. Grazie per la vostra assistenza, a questo punto eliminerò il codice e ruberò praticamente questa funzionalità da lì. Sono ora incuriosito su quale impostazione "_break" su true raggiunga ... hmmmmmm – DrLazer

+0

Segnala la funzione 'Update()' per eseguire l'effettiva interruzione (avevo intenzione di collegare il codice: http: //farseerphysics.codeplex. com/SourceControl/changeset/view/73.518 # 1.086.713) –