Warum müssen Unity-Ebenenmasken Bitverschiebung verwenden?

9

Ich habe endlich herausgefunden, warum meine Ebenenmasken für meinen Bodenkollisionscode nicht funktionierten. Ich habe verwendet NameToLayer(), um die Ebene zu erhalten, die ich brauchte, aber Ebenenmasken verwenden Bitverschiebung, um den Ebenenmaskenwert tatsächlich festzulegen. Dies ist äußerst ungewöhnlich und ich sehe keinen Grund, warum dies im Code dahinter nicht behandelt wird. Warum müssen wir Code wie diesen verwenden:

mask = 1 << LayerMask.NameToLayer("Default");

wenn so etwas:

mask = LayerMask.NameToLayer("Default");

macht intuitiver Sinn und funktioniert ähnlich wie der Rest der Unity-API?

Weltraumstrauß
quelle
2
Die Verwendung der String-Version erfordert mehr Rechenleistung. Ganz zu schweigen davon, dass die Zeichenfolge intern ein Array ist, das ein Referenztyp ist und dem Garbage Collector hinzugefügt wird.
Krythic

Antworten:

2

Dies ist äußerst performant. Das ist alles, was es zu tun gibt - der Vergleich von Strings als offensichtliches Beispiel ist um den Faktor 10 langsamer. Und physikalische Berechnungen müssen sehr optimiert werden. Es ist also gut, dass jemand, der wusste, was los ist, es so geschrieben hat.

Die offensichtliche Folgefrage lautet also: Warum ist dies nicht in eine Hilfsmethode eingewickelt, um die Konvertierung und Bitverschiebung zu handhaben? Ich denke, dass niemand wirklich dazu gekommen ist - ich habe mein eigenes nützliches Hilfsprogramm aufgerollt, und das ist die übliche Praxis.

Jordan Georgiev
quelle
1
Die richtige Entwurfswahl des Unity-Teams wäre gewesen, anstelle einer Zeichenfolge eine Ganzzahl als Indexer zu verwenden. Ich erschrecke, wenn ich darüber nachdenke, wie verrückt der Garbage Collector mit seiner aktuellen Implementierung wahrscheinlich ist, ganz zu schweigen von den String-Array-Zuordnungen.
Krythic
1
Absolut - jedes Array wird am besten durch Ganzzahlen referenziert. Ganzzahlen könnten durch eine einfache Aufzählung leicht menschlich lesbar werden.
Jordan Georgiev
Eine schnelle und schmutzige Implementierung funktioniert, aber für größere Projekte verwende ich [Type Safe] ( assetstore.unity3d.com/de/#!/content/35903 ).
Jordan Georgiev
19

Mit der Bitverschiebung können Sie mehrere Schichten in einer physikalischen Operation berücksichtigen:

 Physics.Raycast(ray, out hitInfo, Mathf.Infinity, layerMask )

Ohne Bitverschiebung könnten Sie in einer Schicht und nur in einer Schicht strahlen. Während der Bitverschiebung können Sie Raycasts in mehreren spezifischen Ebenen durchführen:

layerMask = (1 << LayerMask.NameToLayer("MyLayer1")) | (1 << LayerMask.NameToLayer("MyLayer2")) ;

Sie können auch Raycasts in allen Ebenen mit Ausnahme bestimmter senden :

layerMask = (1 << LayerMask.NameToLayer("MyLayer1")) | (1 << LayerMask.NameToLayer("MyLayer2")) ;
layerMask = ~layerMask;

Wenn Sie sich den "Ebenenmanager" in Unity ansehen , können Ebenen als Indizes eines einfachen eindimensionalen Arrays angesehen werden .


EDIT: Ich habe es noch nie gesehen, aber die LayerMaskKlasse hat eine Utility-Funktion, um die "berechnete" Ebenenmaske mit den Ebenennamen zu erhalten:

Debug.Log( LayerMask.GetMask("UserLayerA", "UserLayerB") ) ;

Angenommen, UserLayerAund UserLayerBsind die zehnte und elfte Schicht. Diese haben einen Benutzerebenenwert von 10 und 11. Um ihren Ebenenmaskenwert zu erhalten, können ihre Namen an GetMask übergeben werden. Das Argument kann entweder eine Liste ihrer Namen oder ein Array von Zeichenfolgen sein, in denen ihre Namen gespeichert sind. In diesem Fall beträgt der Rückgabewert 2 ^ 10 + 2 ^ 11 = 3072.

Link zur Dokumentation: https://docs.unity3d.com/ScriptReference/LayerMask.GetMask.html

Hellium
quelle
2
Sie sollten bitweises ODER |anstelle der Ganzzahladdition verwenden, +wenn Sie Masken zusammenfassen. Die Ganzzahladdition kann zu unerwartetem Verhalten führen.
Wondra
1
Aber LayerMask.NamesToLayers(params string[] layerNames)
andererseits
Guter Punkt @wondra! ;) - Ja, sie hätten diese QBrute ausführen können, aber es ist weitaus flexibler, die Bit-Shiffting-Operation zu verwenden, wenn Sie die Ebenenmaske dynamisch ändern möchten (Hinzufügen, Entfernen von
Ebenen
Bitweise Operationen sind auch extrem schnell. Sie sind eine hervorragende Möglichkeit, große Binärdaten zusammenzusetzen.
Gusdor
Dies beantwortet nicht wirklich die Frage, warum die Methode nicht einfach eine Ebenenmaske anstelle einer Ebenennummer zurückgibt.
Cubic