Jump to content
Unity Insider Forum

Problem mit Massenhafter Instanzierung von Objekten


Thariel

Recommended Posts

Hi

Mache gerade ein Level Editor (Runtime) und Zeichne mit der Maus den Boden, welcher aus einzelnen Tiles besteht. Danach drücke ich ein Button, um die Wände zu generieren.

Das Problem:

Unity scheint nur eine bestimmte Anzahl an Instazierungen zu vollführen.  Immer wieder fehlen völlig willkürlich Wände. Wenn die den Button ganz oft nacheinander klicke, füllen sie die fehlenden Teile langsam.

Hab danach eine Coroutine gemacht und weiter experimentiert.

Wenn ich jetzt zum Beispiel den "Alle Wände Erstellen"-Button klicke, funktioniert es nur mit einem massenhaften Spam von Coroutines:

Die erste ruft in allen Boden Tiles die Funktion "Wände erstellen" auf und jeder Boden hat wiederum eine Coroutine, welcher die Wände erstellt.

Und das schlimmste am ganzen: Ich muss in jeder Schleife ein yield return new WaitForFixedUpdate() aufrufen und in der Haupt-Coroutine gleich 5x hintereinander, damit sie erst weiter macht, wenn der aktuelle Boden fertig ist.

Alles sehr langsam und unelegant, aber nur so wird das Level zuverlässig aufgebaut.

Hat jemand eine Idee, wie das viel besser geht?

Link zu diesem Kommentar
Auf anderen Seiten teilen

Du kannst beliebig viele Dinge in einem Rutsch instanziieren, kein Problem. Wenn da also was schief geht, liegt das Problem in deinem Code.

vor 25 Minuten schrieb Thariel:

Hat jemand eine Idee, wie das viel besser geht?

Nicht, ohne deinen Code anzusehen, denn da steckt - wie gesagt - der Fehler drin.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Da dann poste ich den Code doch mal. Aber nicht lachen, ist noch ein Prototyp :P

Das Script mit dieser Funktion befindet sich auf jedem Boden-Tile.

public void UpdateWalls()
    {
        List<Vector3> directions = new List<Vector3>() { transform.forward, -transform.forward, transform.right, -transform.right };
        foreach (Vector3 direction in directions)
        {
            Vector3 myCenterPoint = transform.position + new Vector3(0, 2, 0);
            Vector3 yourCenterPoint = myCenterPoint + (direction * 4);

            bool neighbourCellHasFloor = false;

            LayerMask mask = LayerMask.GetMask("Level");
            RaycastHit hit;
            if (Physics.Linecast(yourCenterPoint, yourCenterPoint + new Vector3(0, -4, 0), out hit, mask))
            {
                if (hit.transform.gameObject.tag == "Floor")
                    neighbourCellHasFloor = true;
            }

            bool isWall = false;
            GameObject goWall = null;
            if (Physics.Linecast(myCenterPoint, yourCenterPoint, out hit, mask))
            {
                if (hit.transform.gameObject.tag == "Wall")
                {
                    isWall = true;
                    goWall = hit.transform.gameObject;
                }
            }

            if (neighbourCellHasFloor && isWall)
                Destroy(goWall);

            if (!neighbourCellHasFloor && !isWall)
            {
                GameObject go = Instantiate(goWallPrefab, transform.position + positionOffset, Quaternion.identity * Quaternion.Euler(rotationOffset));
                go.transform.rotation = Quaternion.LookRotation(direction, transform.up);
                go.transform.SetParent(transform.parent);
                go.name = goWallPrefab.name;
            }
        }
    }

Und diese Coroutine ruft die Funktion auf jedem Boden-Tile auf:

//gos = alle boden tiles
IEnumerator GenerateWalls(GameObject[] gos)
    {
        foreach (GameObject go in gos)
        {
            go.GetComponent<DesignerObject>().UpdateWalls();
            //yield return new WaitForFixedUpdate();
            yield return null;
        }
        yield return null;
    }

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Hm... auf den ersten (Über)blick sehe ich nichts schlimmes. Ich könnte mir aber wage vorstellen, dass es etwas mit den Raycasts zu tun hat - ist zumindest der erste Ansatz.

Da du hier auf einem Grid zu arbeiten scheinst, ist der Einsatz der Physik-Engine zum Ermitteln von Nachbarfeldern so oder so nicht ganz so prickelnd. Ich würde das Ding nochmal umbauen und dann dein Grid z.B. in einem zweidimensionalen Array abbilden, um darin ganz einfach über Array-Abfragen die Nachbarfelder anzuschauen.

In jedem Fall kannst du - dann oder auch jetzt schon - mit dem Debugger einen Haltepunkt in die Funktion setzen (oder alternativ mit Debug.Log Ausgaben an bestimmten Stellen), mit dem du dir ansehen kannst, was genau schiefläuft. Denn Es wird nicht sein, dass Unity bei Instantiate einfach die Arbeit verweigert - An irgendeinem Teil deines Codes wird wohl entschieden, dass nichts erstellt werden soll. Oder an der falschen Stelle.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Es ist schon ein Grid System. Habe verschiedene Systeme ausprobiert auch deine Variante mir einem Array, das generieren von Wänden ist damit natürlich einfacher, aber mein System hat andere Vorteile. Aber ist ein anderes Thema.

Ist es so, dass nach einem Instantiate() direkt in der nächsten Zeile schon Physik Abfragen durchgeführt werden können oder ist das GameObjekt erst im nächsten Frame da?

Hab mal ein Video zum besseren Verständnis gemacht.

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Leute, jetzt mal ernsthaft! Was passiert da? 😱

Nachdem ich ein Bode-Tile gebaut habe, ruft dieses Script alle Boden-Tiles in der Umgebung auf, um Wände und Pfosten zu aktualisieren:

LayerMask mask = LayerMask.GetMask("Level");
Collider[] cols = Physics.OverlapBox(goGhost.transform.position, new Vector3(4, 4, 4), Quaternion.identity, mask);
foreach(Collider col in cols)
{
   if (col.transform.gameObject.tag == "Floor")
   {
      col.transform.gameObject.GetComponent<DesignerObject>().UpdateWalls();
      col.transform.gameObject.GetComponent<DesignerObject>().UpdatePillars();
   }
}

Wie Wände werden generiert, aber es entsteht nur ein Pfosten:

unity_problem_1a.thumb.jpg.fe3f031493dad0b55964f2f343ed66ab.jpg

Dieses Script generiert die Pfosten:

	List<Vector3[]> points = new List<Vector3[]>(); //um on OnDrawGizmos() zu zeichnen, was genau passiert
    public void UpdatePillars()
    {
        List<Vector3> directions = new List<Vector3>() { transform.forward + transform.right, transform.forward + -transform.right, -transform.forward + transform.right, -transform.forward + -transform.right };

        int round = 0;
        foreach (Vector3 direction in directions)
        {
            Debug.Log("Round " + round + " with direction " + direction + " is starting...");

            Vector3 myCenterPoint = transform.position + new Vector3(0, 2, 0);
            Vector3 myCornerPoint = myCenterPoint + (direction * 2);

            points.Add(new Vector3[] {myCenterPoint, myCornerPoint });

            int pillarCount = 0;
            int wallCount = 0;
            GameObject goPillar = null;

            LayerMask mask = LayerMask.GetMask("Level");
            Collider[] cols = Physics.OverlapSphere(myCornerPoint, 0.5F, mask);

            foreach (Collider col in cols)
            {
                Debug.Log("Round " + round + ": Collider found: " + col.transform.gameObject.name); 
                if (col.transform.gameObject.tag == "Wall")
                    wallCount++;

                if (col.transform.gameObject.tag == "Pillar")
                {
                    pillarCount++;
                    goPillar = col.transform.gameObject;
                }
            }

            if (wallCount == 0 && pillarCount > 0)
            {
                Debug.Log("Round " + round + ": Destroy Pillar");
                Destroy(goPillar);
            }

            if (wallCount > 0 && pillarCount == 0)
            {
                GameObject go = Instantiate(goPillarPrefab, transform.position, Quaternion.identity);
                Quaternion lookRotation = Quaternion.LookRotation(direction, transform.up);
                lookRotation *= Quaternion.Euler(0, 45, 0); //damit die zelle um 90° winkel ist
                go.transform.rotation = lookRotation;
                go.transform.SetParent(transform.parent);
                go.name = goPillarPrefab.name;
                Debug.Log("Round " + round + ": Pillar Created.");
            }
            Debug.Log("Round " + round + " finished.");
            round++;
        }
    }

Ich zeichne auch genau auf, WO OverlapSphere() wirkt und auch die Richtung und die Colliders sind ersichtlich (alle haben korrekten Layer + Tag):

private void OnDrawGizmosSelected()
    {
        foreach (Vector3[] point in points)
        {
            Gizmos.DrawLine(point[0], point[1]);
            Gizmos.DrawSphere(point[1], 0.5F);
        }
    }

unity_problem_1b.thumb.jpg.dedc8c1f42c07d44c8105648ae2349ac.jpg

Aber die Konsole gibt völlig unterwartete Ausgaben:

unity_problem_1c.jpg.b8fb216f48000e340fbbe877bdfd86cd.jpg

In der ersten "direction" findet es 4 Wände, in der zweiten WIEDER 4 Wände UND ein Pfosten und dann aber nichts mehr...

Versteh ich nicht 🤔

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Jetzt funktioniert es. Weiss zwar nicht warum genau, aber nach dieser Änderung funktioniert alles perfekt, auch ohne Coroutine.

Das Funktioniert NICHT:

GameObject go = Instantiate(goPillarPrefab, transform.position, Quaternion.identity);
Quaternion lookRotation = Quaternion.LookRotation(direction, transform.up);
lookRotation *= Quaternion.Euler(0, 45, 0); //damit die zelle um 90° winkel ist
go.transform.rotation = lookRotation;

Das FUNKTIONIERT:

Quaternion lookRotation = Quaternion.LookRotation(direction, transform.up);
lookRotation *= Quaternion.Euler(0, 45, 0); //damit die zelle um 90° winkel ist
GameObject go = Instantiate(goPillarPrefab, transform.position, lookRotation);

Einfach die Rotation vorher berechnen und bei Instantiate() direkt übergeben.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

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

×
×
  • Neu erstellen...