Jump to content
Unity Insider Forum

[Gelöst]Trigger vs. Distance


Jomnitech

Recommended Posts

Hallo Zusammen

Bin mir nicht sicher ob ich besser eine Trigger Abfrage machen soll, oder besser über Vector3.Distance die Abfrage mache.
Konkret geht es um Gegenstände die der Spieler aufheben kann. Bei der Distance Abfrage würde also nur abgefragt werden ob der Spieler in der nähe ist. Bei der Trigger Variante würden die doch auch sich automatisch gegenseitig abfragen? Ausserdem bräuchte ich, damit es damit gut funktioniert onTriggerEnter, onTriggerExit, und onTriggerStay. Ist das aber immer noch besser als über die Distance? Meine mal gelesen zu haben das eine Distanz-Abfrage super schlecht sei, finde aber den Threat nicht mehr...

Link zu diesem Kommentar
Auf anderen Seiten teilen

Stimmt schon. Eine Distance-Abfrage braucht einiges an Performance. Es ist aber, wie immer, eine Frage der Dosis.
Wenn nur wenige Abfragen gemacht werden, ist das alles kein Problem. Wenn du aber viele Objekte im Spiel hast, die jedes Frame über abfragen wo der Spieler ist, dann wirst du das im Profiler sehen. Da werden einige Prozent deiner Leistung drauf gehen. Du könntest aber die Objekte einfach über eine Coroutine nur alle 0.5 Sekunden eine Messung machen lassen.
Ich persönlich würde die Trigger nutzen. Die feuern ein Event, wenn es soweit ist und brauchen sonst eigentlich nur wenig Leistung. OnTriggerStay brauchst du in diesen Fällen gar nicht. Du setzt einfach eine Hilfsvariable auf true, sobald das OnTriggerEnter Event ausgeführt wird, und wieder auf false wenn das OnTriggerExit Event ausgeführt wird.
Über diese Variable kannst du dann z.B. ein Leuchten des Objektes steuern. Außerdem triggert das Event in beide Richtungen, wie du ja selber geschrieben hast. Der Player kann über ein Trigger Event genauso etwas von dem Objekt erfahren.
 

Eine ganz wichtige Sache ist auch, dass eine Distance Messung nichts darüber aus sagt, ob evtl. eine Wand zwischen dem Player und dem Objekt liegt. Einen Trigger kannst du einfach so positionieren, dass er nicht durch die Wand reichen würde.

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor einer Stunde schrieb malzbie:

Eine Distance-Abfrage braucht einiges an Performance.

Naja, so wirklich viel ist das auch wieder nicht. Das teuerste an der Sache ist das Wurzelziehen, und das kann man bei Vergleichen sogar weglassen. Schauen wir doch mal ganz genau:

public static bool CheckIfCloserThan(Vector3 a, Vector3 b, float maxDistance)
{
  var maxDistanceSquared = maxDistance * maxDistance;
  
  var distanceSquared = (a.x - b.x) * (a.x - b.x) +
                        (a.y - b.y) * (a.y - b.y) +
                        (a.z - b.z) * (a.z - b.z);
  
  return distanceSquared <= maxDistanceSquared;
}

Diese Funktion checkt, ob der Abstand zwischen a und b kleiner-gleich der gegebenen Maximaldistanz ist. Wir benutzen in der Mitte nicht einfach (a - b).sqrMagnitude, weil das etwa 4-Mal so langsam ist.

Es bleibt damit bei vier Multiplikationen, sechs Subtraktionen und einem Kleiner-gleich-Vergleich. Das sind elf FLOPS. Dagegen kann ein Physik-Check mit Collidern gar nicht ankommen. Da ist ja erstmal der AABB Check. Der alleine ist im Zweifelsfall ja auch schon gerne mit neun FLOPS und drei Bool-Abgleichen am Start. Und danch kommt erstmal der richtige Kollisionscheck.

Wenn es also wirklich nur um den Abstand zwischen zwei Punkten geht, nicht immer gleich verrückt machen lassen. So viel ist da gar nicht los, und mit einer simplen Funktion wie der da oben bist du in jedem Fall schneller unterwegs als mit der Physik-Engine.

Einer Kernaussage von @malzbie muss ich außerdem grundsätzlich zustimmen:

vor einer Stunde schrieb malzbie:

Wenn nur wenige Abfragen gemacht werden, ist das alles kein Problem.

Und ich würde da hinzufügen: Alles unter fünfstellig zählt für mich hier als "wenige". Mal so als Vergleich: Meine Funktion oben läuft bei mir auf dem PC in unter 10ns. Eine Nanosekunde entspricht 0,000001ms. Das heißt, dass du die Funktion schon 100.000-Mal aufrufen musst, damit ihre Benutzung 1ms kostet. Und das sequenziell, denn solche Berechnungen kannst du wunderbar parallelisieren oder batchen.

Performance ist erst dann ein Problem, wenn es ein Problem wird. Zu versuchen, irgendwelche Performance-Optimierungen zu machen, bevor der Profiler überhaupt Probleme aufzeigt, ist schlichtweg keine gute Idee. Kümmer dich darum, dass dein Spiel funktioniert, achte darauf, dass du gute Code-Qualität hast (Robustheit, Modularität, geringe Redundanz...). Und wenn du dann irgendwann Performance-Probleme feststellst, dann kannst du mit dem Profiler die Problemstelle finden und verbessern.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Habe gerade ein Spiel laufen, deshalb sind die absoluten Werte etwas hoch, geht ja aber nur um die relativen ;)

  • Meine Funktion: 15ns
  • (a - b).sqrMagnitude: 48.2ns
  • Vector3.Distance: 65.2ns

Die Benchmark-Werte schwanken natürlich immer etwas. Und wie gesagt, das Spiel im Hintergrund scheint da etwa +55% draufzupacken. Ansonsten braucht Vector3.Distance natürlich etwas länger als sqrMagnitude weil's dasselbe mit Wurzel hintendran ist.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Danke @malzbie und @Sascha für eure Antwort

vor 2 Stunden schrieb malzbie:

OnTriggerStay brauchst du in diesen Fällen gar nicht. Du setzt einfach eine Hilfsvariable auf true, sobald das OnTriggerEnter Event ausgeführt wird, und wieder auf false wenn das OnTriggerExit Event ausgeführt wird.

Stimmt, das könnt ich wirklich mit einer Boolien reduzieren, mein Gedanke war einfach ob ich das jetzt über das TriggerStay event mache oder die Boolien in einem Update abfrage ist doch irgendwie gleich.

vor einer Stunde schrieb Sascha:

public static bool CheckIfCloserThan(Vector3 a, Vector3 b, float maxDistance) { var maxDistanceSquared = maxDistance * maxDistance; var distanceSquared = (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y) + (a.z - b.z) * (a.z - b.z); return distanceSquared <= maxDistanceSquared; }

Danke für dein Input, allerdings geht das über mein Wissen hinaus, ich habe keine Ahnung wie ich das implementiere, dass es mir etwas rausgibt womit ich etwas triggern kann.

vor einer Stunde schrieb Sascha:

Performance ist erst dann ein Problem, wenn es ein Problem wird. Zu versuchen, irgendwelche Performance-Optimierungen zu machen, bevor der Profiler überhaupt Probleme aufzeigt, ist schlichtweg keine gute Idee

Stimmt schon, aber ist eben auch ein zweischneidiges Schwert. Einfach drauf los machen ohne Hand und Fuss, kann sich dann eben später wenn es dann zum Problem wird, übel rächen. Wenn das Fundament nicht stimmt um man vieles neu machen muss, macht das dann auch keine Spass.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Gerade eben schrieb Jomnitech:

Einfach drauf los machen ohne Hand und Fuss, kann sich dann eben später wenn es dann zum Problem wird, übel rächen. Wenn das Fundament nicht stimmt um man vieles neu machen muss, macht das dann auch keine Spass.

Deswegen sollst du auf Code-Qualität achten. Wenn dein Code gute Qualität hat und kein langer Spaghetticode ist, kannst du da sauber einzelne Teile nach und nach dran verbessern. Code muss einfach nicht in der ersten Version perfekt sein - das ist völlig unrealistisch. Guter Code erlaubt es dir entsprechend, da lange und oft dran zu arbeiten, ohne dass man irgendwann ein unwartbares Monster hat.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Gerade eben schrieb Jomnitech:

Wirklich beieindruckend, strebst du den pico Bereich an?

Naja, wie gsagt, +55% :)

Ach ja...

vor 4 Minuten schrieb Jomnitech:

ich habe keine Ahnung wie ich das implementiere

Naja... so:

if (CheckIfCloserThan(transform.position, otherTransform.position, 100))
{
  Debug.Log("Der andere ist näher als 100m!");
}

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

@Sascha

jetzt hab ich doch noch eine Frage dazu. Wie bekomme ich es raus wenn man nicht im Radius ist?
Weder eine else noch eine if(!CheckIfCloserThan) bringt mir das gewünschte Ergebniss. Die Abfrage läuft auch dann weiter wenn ich im Bereich bin.

Also nicht nur abfragen ob man im Bereich ist, sondern auch wenn man den Bereich wieder verlässt.

Link zu diesem Kommentar
Auf anderen Seiten teilen

if(!CheckIfCloserThan) ist schon richtig. Wenn du Betreten und Verlassen checken willst, musst du dir merken, wie der vorherige Status war.

private bool wasInside = false;

private void Update()
{
  var isInside = CheckIfCloserThan( ... );
  
  if (isInside && !wasInside)
  {
    OnEnterRange();
  }
  else if (!isInside && wasInside)
  {
    OnExitRange();
  }
  
  wasInside = isInside;
}

private void OnEnterRange()
{
  // ...
}

private void OnExitRange()
{
  // ...
}

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

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

×
×
  • Neu erstellen...