Jump to content
Unity Insider Forum

Raycasting im Bereich


Kojote

Recommended Posts

Grüße!

Es gibt ja die schöne Raycasting funktion. Diese Benutze ich bei meinem Spawner schon zur Nachkalkulation der Y-Achse:

        private Vector3 CalculateYPosition(Vector3 spawnPoint) {
            // Diese Methode ermittelt den genauen Y-Wert zur Positionierung eines Prefab-Objekts.

            Vector3 sp = new Vector3();

            RaycastHit hit;

            Ray ray = new Ray(spawnPoint + Vector3.up * 100, Vector3.down);

            if (Physics.Raycast(ray, out hit, Mathf.Infinity, mask)) {
                if (hit.collider != null) {
                    sp = new Vector3(spawnPoint.x, hit.point.y, spawnPoint.z);
                }
            }

            return new Vector3(sp.x, sp.y, sp.z);
        }

Das kleine Code-Schnipsel schaut aber nur, wegen der Y-Position. Bei der Positionierung möchte ich auch abfragen, ist in einem bestimmten Umkreis ein störendes Objekt, wie ein Felsen, ein Spieler oder sonst was. Gibt es da auch eine Funktion wie Raycast, was einen Bereich absucht, am besten eine Sphere?

Danke schonmal! :)

Grüße von Kojote

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ja, Physics.OverlapShere passt prima, hätte aber noch mal ne Nachfrage.

Ich hab mir jetzt diese Methode geschrieben:

        private bool PositioningCheck(Vector3 spawnPoint) {
            // Diese Methode ermittelt, ob ein Objekt an dieser Position gespawnt werden kann. Wenn TRUE, ist nichts im Weg und es kann gespawnt werden, bei FALSE, ist ein Objekt im Weg.

            Collider[] hitColliders = Physics.OverlapSphere(spawnPoint, 10, terrainLayerMask, QueryTriggerInteraction.Ignore);
            int collisions = 0;

            for (int i = 0; i < hitColliders.Length; i++) {
                if (notAllowedColliosionsLayerMask == (notAllowedColliosionsLayerMask | (1 << i))) {
                    collisions++;
                    Debug.Log(notAllowedColliosionsLayerMask.value);
                }
            }

            if (collisions == 0) {
                return true;
            } else {
                return false;
            }
        }

In der ersten Zeile führe ich Physics.OverlapShere aus, genau an dem Punkt, an dem gespant werden soll, im Umkreis von 10 und er soll mein Bodenterrain ignorieren und alle Trigger.

Zurück bekomme ich ein Array-Collider-Objekt. Nun wollte ich hier gern ermitteln, ob ein ausgewählter Layer getroffen wurde. Es können unterschiedliche Layer  sein, deswegen kann ich ihn nicht mit Zahlen beschreiben. Alle Layer die nicht getroffen dürfen sind unter "notAllowedCollisionLayerMask" hinterlegt. Wollte den Intager "collisions" als Zählvariable nutzen, um dann am Ende abzufragen, ob ein Layer getoffen wurde, dann halt return.

Aber irgendwie ist mein if-Statemant blödsinn.^^"

Was mir gerade beim schreiben hier einfällt, man könnte die Value Werte vergleichen, eine Schleife in einer Schleife, aber gibts da noch ne andere komplexe Möglichkeit?

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor einer Stunde schrieb Kojote:

Was mir gerade beim schreiben hier einfällt, man könnte die Value Werte vergleichen, eine Schleife in einer Schleife, aber gibts da noch ne andere komplexe Möglichkeit?

Das kann man so machen, man kann aber auch Linq benutzen. Was besser für die Lesbarkeit ist, ist von Fall zu Fall zu beurteilen.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Eigenartig, hier versteh ich was nicht.

Ich habs nun so geschrieben:

        private bool PositioningCheck(Vector3 spawnPoint) {
            // Diese Methode ermittelt, ob ein Objekt an dieser Position gespawnt werden kann. Wenn TRUE, ist nichts im Weg und es kann gespawnt werden, bei FALSE, ist ein Objekt im Weg.

            Collider[] hitColliders = Physics.OverlapSphere(spawnPoint, 10, allowedColliosionsLayerMask, QueryTriggerInteraction.Ignore);
            int collisions = 0;

            for (int i = 0; i < hitColliders.Length; i++) {
                Debug.Log(hitColliders[i].gameObject.layer.ToString());
            }

            if (collisions == 0) {
                return true;
            } else {
                return false;
            }
        }

allowedCollisionsLayerMask ist ja die Maske, wo in der Dokumentation steht, alle Masken die hier eingetragen sind, werden ignoriert:

Zitat
layerMask A Layer mask that is used to selectively ignore colliders when casting a ray.

So, ich habe nun im Inspector auf Everything geklickt. Demzufolge sollte der Debug mir mitteilen, dass es keine Collisionen gibt. Aber, zwei Collisionen, Terrain und und Default.

Klicke ich nun auf Nothing, gibts keine Collision. Muss ich das verstehen? Oder hab ich hier gerade nen Denkfehler. Ich soll doch bei den Masken alles anwählen, was ignoriert werden soll, oder?

Link zu diesem Kommentar
Auf anderen Seiten teilen

vor 4 Stunden schrieb Kojote:

allowedCollisionsLayerMask

Der Name sagt schon genau das Gegenteil. Ich sehe auch, warum du das so aus dem Satz rausgelesen hast, aber da steht nur, dass damit Collider ignoriert werden - nicht, dass die Layer in der Layermask die ignorierten sind.

vor 4 Stunden schrieb Kojote:

Ich soll doch bei den Masken alles anwählen, was ignoriert werden soll, oder?

Genau umgekehrt.

Wenn du dich fragst, warum die das jetzt so in der Dokumentation formuliert haben, dann versuch mal denselben Satz in andersherum zu formulieren :)

Link zu diesem Kommentar
Auf anderen Seiten teilen

Also so 100%ig funktionier das net.

Ich hab das Script zum spawnen:

        private IEnumerator SpawnObjectAtSpawnpointCoroutine() {
            if (spawnSettings == Spawn_Settings.Random) {
                Vector3 spawnPoint = CalculateSpawnVectorAtSpawnPointWithRandom();

                if (useCollisionCheck) {
                    if (PositioningCollisionCheck(spawnPoint) == false) {
                        yield return new WaitForSeconds(3f);
                        yield return null;
                    }
                }

                if (useHiddenSpawnCheck) {
                    if (HiddenSpawnCheck(spawnPoint) == false) {
                        yield return new WaitForSeconds(3f);
                        yield return null;
                    }
                }

                GameObject objectToSpawn = pool.Dequeue();

                objectToSpawn.SetActive(true);

                objectToSpawn.transform.position = CalculateYPosition(spawnPoint);
                objectToSpawn.transform.rotation = RandomQuaternion();

                isSpawning = false;
            }

Und hier der Collision-Check:

        private bool PositioningCollisionCheck(Vector3 spawnPoint) {
            Collider[] hitColliders = Physics.OverlapSphere(spawnPoint, collisionSizeCheck, notAllowedColliosionsLayerMask, QueryTriggerInteraction.Ignore);

            if (hitColliders.Length == 0) {
                return true;
            } else {
                return false;
            }
        }

Problem nun, alle Spawnable Objects besitzen den Layer Animal, mit einem Spawn-Point würde das bedeuten, dass ein Object spawnen kann, die anderen, aber in die Sicherung rennen, 3 Sekunden warten und wieder in die Sicherung rennen, bis das erste Object den Einflussbereich verlassen hat. Der Herr meint aber, alle Objects können spawnen und gibt immer true zurück. 😕

EDIT: Ne Moment! Falsch er gibt false zurück, was richtig wäre, aber das Return funktioniert net in der Coroutine. Sinn der Sache war, nach und nach alle Checks durchzugehen und wenns einmal false gibt, zurück an den Anfang und nen neuen Spawn-Point berechnen.

EDIT 2: Irgendwas sagt mir gerade das "yield return null;" in der Coroutine nicht die selbe Funktion wie "return;" in normalen Methoden hat oder lieg ich da falsch? 🧐

Link zu diesem Kommentar
Auf anderen Seiten teilen

Meinst du eher so hier:

private IEnumerator SpawnObjectAtSpawnpointCoroutine() {
            if (spawnSettings == Spawn_Settings.Random) {
                Vector3 spawnPoint = new Vector3();
                bool checker = false;

                while (!checker) {
                    spawnPoint = CalculateSpawnVectorAtSpawnPointWithRandom();

                    bool checkCollision = false;
                    bool checkHidden = false;

                    // Wenn ein Collision-Check überprüft werden soll, wird dies in dieser if-Abfrage ausgeführt.
                    if (useCollisionCheck) {
                        if (PositioningCollisionCheck(spawnPoint) == false) {
                            checkCollision = false;
                            yield return new WaitForSeconds(2f); // Wichtig! Die Wartezeit sorgt dafür, dass die Schleife nicht einfriert.
                        } else {
                            checkCollision = true;
                        }
                    } else {
                        checkCollision = true;
                    }

                    // Wenn ein Hidden-Spawn-Check durchgeführt werden soll, wird dies in dieser if-Abfrage ausgeführt.
                    if (useHiddenSpawnCheck) {
                        if (PositioningCollisionCheck(spawnPoint) == false) {
                            checkHidden = false;
                            yield return new WaitForSeconds(2f); // Wichtig! Die Wartezeit sorgt dafür, dass die Schleife nicht einfriert.
                        } else {
                            checkHidden = true;
                        }
                    } else {
                        checkHidden = true;
                    }

                    // Sind sowohl Collision-Check als auch Hidden-Spawn-Check positiv ausgefallen, wird die Schleife verlassen, es kann gespawnt werden.
                    if (checkCollision == true && checkHidden == true) {
                        checker = true;
                    }
                }

                GameObject objectToSpawn = pool.Dequeue();

                objectToSpawn.SetActive(true);

                objectToSpawn.transform.position = CalculateYPosition(spawnPoint);
                objectToSpawn.transform.rotation = RandomQuaternion();

                isSpawning = false;
            }

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Keine Ahnung, der Code ist inzwischen zu komplex um das so einfach sagen zu können.

Aber wenn du in einem einigermaßen regelmäßigen Interval etwas machen willst, ist der Grundaufbau für die Coroutine in der Tat so etwas:

private IEnumerator DoStuff()
{
  var wait = new WaitForSeconds(5f);
  
  var trying = true;
  while (trying)
  {
    if (SomethingWorks())
    {
      trying = false;
    }
    else
    {
      yield return wait;
    }
  }
}

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

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

×
×
  • Neu erstellen...