Jump to content
Unity Insider Forum

Pixel genaue Kollisionserkennung


Mark

Recommended Posts

Malzbie wollte ja gern drüber plaudern, wie genau ich die Pixelgenaue Kollisionserkennung hier:

 

https://www.youtube.com/watch?v=-3IGVfT8h20

 

realisiert habe.

 

Das System funktioniert so:

 

Ich habe eine Componente welche einfach mal PixelCollider heißt, aber selbst kein wirklicher Collider ist. Diese Komponente erstellt ein verstecktes GO als Child vom aktuellen GO.

Auch erstellt es einen versteckten deaktivierten Collider im HauptGO (damit das GO einen Collider hat den man überall hin als Strohman geben kann, ist nicht nötig aber ist brauchbar).

 

Dieses GO wird ProxyGO genannt und hat eine Trigger (!) CirlceCollider Komponente welche das Haupt GO umhüllt (es tut auch jede andere Collider2D Komponente).

Auch hat das ProxyGO ein Kinematic Rigidbody2D und eine Custom Komponente welche mir TriggerEnter/Exit/Stay Events an meine PixelCollider Komponente im HauptGO sendet.

 

Der PixelCollider empfängt diese Messages. Schaut sich an was fürn Collider mit dem ProxyGO in Berührung kam und führt je nach Typ einen Handler aus.

 

Die Handler generieren ein Rect aus dem Ergebniss der Schnittfläche von 2 Rects, dem BoundingRect des PixelColliders und dem Bounding Rect des anderen Colliders. Dieses finale Rect

wird von mir in relation des PixelColliders gebracht damit jeder Punkt in diesem Rect ein Kollisionspixel meines PixelColliders entspricht.

 

Nun iteriere ich über jeden Punkt dieses Rects und frage zuerst den PixelCollider ob er da solid ist (einen aktiven Kollisionspixel hat) und wenn ja dann frage ich noch den anderen Collider

ob er an dem Punkt (muss in den Space des anderen Colliders transformiert werden) solid ist. Im Fall in dem der andere Collider ein Collider2D ist sieht das ganz simpel so aus:

 

protected bool IsSolid(T other, Vector2 position)
{
 return other.OverlapPoint(position);
}

 

Wenn beide Collider an der Stelle solid sind findet eine Kollision an diesem Punkt statt (die Behandlung ob wir ein trigger sind oder nicht findet später am Ende statt).

Ich generiere für diesen Punkt meine Kollisionsdaten (Normale, Weltpunkt, Collider) und breche ab oder sammle weiter (je nach Modus den ich angeben kann).

 

Die Normalen für den PixelCollider generiere ich indem ich am punkt der Kollision ausgehend die Nachbarpixel in einem angebbarem Radius (NormalAccuracy) prüfe und mir die Richtung von

Kollisionspixel zu Prüfpixel ein Zwischenresultats Vector addiere, aber nur wenn der zu prüfende Pixel nicht solid ist. Je nach Entfernung zum Kollisionspixel wird die Richtung anders gewichtet.

 

Am Ende wird dieser Vector normalisiert und ich habe meine Normale.

 

Wenn einer der beteiligten Collider ein Trigger ist wird ein Event an alle beteiligten gesendet

Wenn beide Collider keine Trigger sind, wird vor dem Senden der entsprechenden Nachrichten noch das Ergebniss der Kollision berechnet.

Abprallen, Friction, etc.

 

Aber hier kann es passieren dass Objekte sich durchstoßen, weswegen die Position aller beteiligten Objekte korrigiert werden muss. Idealerweise werden die Objekte so positioniert

dass sich keiner mehr berührt, dies führt aber dazu dass man ein Objekt eventuell in ein anderes bisher unbeteiligtes schiebt, was sehr hässliche Effekte hat.

 

Meine Lösung dazu sieht so aus dass jedes GO was sich an einer Kollision mit einem PixelColloder beteiligen könnte eine kleine spezielle Komponente benötigt (GOs mit dem PixelCollider selbst benötigen das nicht mehr).

Diese Komponente gibt unter anderem an wie genau die generierenden Normalen sein sollen und speichert die Position des GOs am Ende des PhysikFrames

 

yield return new WaitForFixedUpdate(); <- wird am Ende des Frames ausgeführt, quasi ein LateFixedUpdate

 

Bei einer Kollision werden einfach alle beteiligten GOs an die letzte bekannte Position zurück gesetzt, die Position die ermittelt wurde BEVOR der PhysikFrame die Position verändert hat.

Dies hat zwar den Effekt dass Hochgeschwindigkeitsobjekte merkbar vor der eigentlichen Kollision stoppen/abprallen, aber das ist besser als die Alternative.

 

man könnte komplexere Lösungen versuchen, aber bisher ist das Resultat zufriedenstellend.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ah, ok! Das habe ich soweit verstanden. Hatte früher Mengenlehre und kenne mich mit den Schnittmengen aus! :D

Danke für deine Ausführung. :)

Aber wie hast du definiert, ob ein Pixel solid ist oder nicht? Bei deiner Demo schießt du ja auch die Pixel weg, es wird also doch auch die Textur verändert und die Plätze wo die (wahrscheinlich Solid) Pixel waren sind dann ja nicht mehr solid.

Fragst du die Pixel der Texturen ab und definierst du die Eigenschaften (solid oder nicht) über die Farbe?

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich habe in Pix eine ModificableTexture Komponente die nur die Textur die gerendert wird verändert. Die ist separat zur PixelCollider Komponente, der PixelCollider bekommt eine separate Textur (später wirds ein wenig Kooperation geben damit das nicht notwendig ist) und zieht aus dieser Textur pro Pixel ein Bool heraus der besagt ob Solid oder nicht. Dies macht er indem er den Alpha der Pixel anschaut, solid = color.a != 0.

 

Der PixelCollider hat also nurn Array von bools.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

Dieses Thema ist jetzt archiviert und für weitere Antworten gesperrt.

×
×
  • Neu erstellen...