Hallo,
ich programmiere für ein Schulprojekt eine Simulation. Dabei hat man eine Kugel (Planet) und Objekte(Haus, Baum, ...), die zufällig platziert werden und dann per Gravitation angezogen werden.
Ich habe schon die Routine, die das Spawnen übernimmt. Fehlen tut mir eine Kollisionsprüfung, ob das neu zu spawnende Objekt am Punkt x,y,z mit der Größe "a,b,c" platziert werden darf, OHNE mit einem bereits vorhandenen Objekt zu kollidieren.
Weil ein Haus auf einem Baum wäre ja komisch.
Aus diesem Grund soll vor dem Spawnen eine Prüfung stattfinden, die Schwierigkeit liegt da einfach an der Kugel 😠
Ich habe den Mittelpunkt (setzen wir mal 0,0,0), den Radius, die Position des Objektes und die Größe des Objektes.
Ich habe bereits angefangen, komme allerdings bei der Kollisionsprüfung nicht mehr weiter:
public class WorldGenerator : MonoBehaviour
{
CalcCoordinates calculator; // Hilfsklasse
GameObject planet; // Der Planet
public GameObject[] planetPrefabs; // Objekte auf dem Planeten
public GameObject[] skyPrefabs; // Objekte in der Luft
public GameObject[] spacePrefabs; // Objekte im All
void Start()
{
calculator = GameObject.FindGameObjectWithTag("Calculator").GetComponent<CalcCoordinates>();
planet = GameObject.FindGameObjectWithTag("Planet");
SpawnObjects(planet, planetPrefabs, PlayerPrefs.GetInt("planetObjectsCount"), 50); // SpawnRoutine
SpawnObjects(planet, skyPrefabs, PlayerPrefs.GetInt("skyObjectsCount"), 150);
SpawnObjects(planet, spacePrefabs, PlayerPrefs.GetInt("spaceObjectsCount"), 200);
}
private void SpawnObjects(GameObject planet, GameObject[] prefabs, float objectCount, float distanceToPlanet)
{
List<gameObject> spawnObjectsList = new List<gameObject>(); // speichere alle bereits erstellten Objekte
for (int i = 0; i < objectCount; i++)
{
gameObject spawnObject = prefabs[Random.Range(0, prefabs.Length)]; // Welches Objekt soll gespawnt werden?
Vector3 spawnPos = calculator.CalcPointOnSphere(distanceToPlanet); // Wo soll das Objekt gespawnt werden?
if(CheckSpawnPoint(spawnObjectsList, spawnObject, spawnPos)) // Kollisionsprüfung
{
gameObject objectToSpawn = (gameObject)Instantiate(spawnObject, spawnPos, Quaternion.identity); // neues Objekt
spawnObjectsList.Add(objectToSpawn); // das neue Objekt der Liste hinzufügen
}
}
}
private bool CheckSpawnPoint(List<gameObject> spawnObjectsList, GameObject objectToSpawn, Vector3 spawnPos)
{
foreach(gameObject listObject in spawnObjectsList) // alle bereits gespawnten Objekte
{
if(..) // wenn das zu spawnende objekt mit einem bereits vorhandenen Objekt kollidieren würde ...
{
return false;
}
}
return true;
}
}
Ich hoffe die Kommentare helfen ein wenig, manche Unity-Begriffe besser zu verstehen.
Sonst einfach melden 🙂
Bin zwar kein Unity-Experte, aber prüfe mal ob dir Physics.OverlapSphere dabei hilft?
Das weiß ich auch nicht, dadurch würde ich ja erstmal nur die Objekte geliefert bekommen.. Die habe ich ja aber bereits eigentlich
Was ich meine in Pseudocode:
do
{
Platziere GameObject an zufälliger Position
} while(es gibt überlappende Objekte)
Hey,
durch die Schleife hängt sich das Projekt auf.
Also im Code
CalcCoordinates calculator;
GameObject planet;
public GameObject[] planetPrefabs;
public GameObject[] skyPrefabs;
public GameObject[] spacePrefabs;
void Start()
{
calculator = GameObject.FindGameObjectWithTag("Calculator").GetComponent<CalcCoordinates>();
planet = GameObject.FindGameObjectWithTag("Planet");
SpawnObjects(planet, planetPrefabs, PlayerPrefs.GetInt("planetObjectsCount"), 50);
SpawnObjects(planet, skyPrefabs, PlayerPrefs.GetInt("skyObjectsCount"), 150);
SpawnObjects(planet, spacePrefabs, PlayerPrefs.GetInt("spaceObjectsCount"), 200);
}
private void SpawnObjects(GameObject planet, GameObject[] prefabs, float objectCount, float distanceToPlanet)
{
for (int i = 0; i < objectCount; i++)
{
Instantiate(prefabs[Random.Range(0, prefabs.Length)], calculator.CalcPointOnSphere(distanceToPlanet), Quaternion.identity);
}
}
hatte ich innerhalb der for-Schleife eingebaut:
Collider[] colliders = ...
do
{
Instantiate...
}
while
{
colliders != null;
}
Hi Garzec,
du hast dir eine Endlosschleife gebaut, da sich die Bedingung colliders != null
nicht ändert.
Außerdem ist die Verwendung von Collidern evtl. nicht das richtige für dein Vorhaben. Collider erkennen ja Kollisionen während des Spiels, also zwischen sich bewegendenden, dynamischen Objekten. Du willst ja, wenn ich das richtig verstehe, nur statische Objekte erzeugen. Da würde es reichen, bei der Erstellung zu überprüfen, ob ein Objekt mit einem anderen kollidiert.
Evtl. würde es den Vorgang erleichtern, wenn du statt 3D-Kollisionstests die Tests für die Objekte auf der Planetenoberfläche in 2D berechnen kannst, beispielsweise indem du die Koordinaten auf der Kugel mit einer Merkator- o.ä. Projektion in eine 2D-Fläche transformierst.
Weeks of programming can save you hours of planning
Ich hatte gehofft das Ganze mathematisch zu lösen. Alle vorhandenen Objekte werden in einer Liste gespeichert, sodass nur noch prüfen muss, ob das Objekt an dem Platz keinen anderen "Würfel" schneidet, der dort schon platziert ist.
Natürlich löst man soetwas mathematisch. Aber du kannst auch die eingebauten Features benutzen, z.B. indem du prüfst, ob die BoundingBoxes der Objekte in der Liste mit der BoundingBox eines neuen Objekts kollidieren. Beispielsweise mit der Bounds.Intersects-Methode.
In 2D wäre diese Überprüfung performanter als in 3D, was sich bei sehr vielen Objekten bemerkbar machen würde. Aber dazu müßtest du deine 3D-Koordinaten erstmal in 2D transformieren.
Weeks of programming can save you hours of planning
Also da das Ganze "nur" ein Schulprojekt ist und ich das Ganze eher verstärkt dokumentieren muss, beschränke ich mich auf 100 Objekte. Einfach, damit mein alter Laptop das beim Vorstellen noch schafft.
Deine genannte Methode kannte ich noch gar nicht. Das klingt so, als wäre das Thema gar nicht so gigantisch wie gedacht 😄
Oder doch?
Bis jetzt hab ich erstmal meine alten Abi Bücher rauskramen müssen und viel Mathe auffrischen müssen 😛
Habe grade leider noch keinen Zugriff aufs Projekt, habe aber schonmal eine Routine versucht:
private void SpawnObjects(GameObject planet, GameObject[] prefabs, float objectCount, float distanceToPlanet)
{
List<Bounds> objectsBoundsList = new List<Bounds>();
for (int i = 0; i < objectCount; i++)
{
GameObject spawnObject = prefabs[Random.Range(0, prefabs.Length)];
Vector3 spawnPos = calculator.CalcPointOnSphere(distanceToPlanet);
Bounds tempSpawnBounds = new Bounds(spawnPos, spawnObject.GetComponent<Renderer>().bounds.size);
foreach (Bounds objectBounds in objectsBoundsList)
{
if (objectBounds.Intersects(tempSpawnBounds))
{
continue;
}
}
GameObject objectToSpawn = (GameObject)Instantiate(spawnObject, spawnPos, Quaternion.identity);
objectsBoundsList.Add(new Bounds(objectToSpawn.transform.position, objectToSpawn.GetComponent<Renderer>().bounds.size));
}
}
Testen kann ichs leider noch nicht...
Nachtrag:
Konnte den Code jetzt mal testen. Der Debugger kommt bei 100 Objekten ca. 1-2 mal an die Stelle, die den Code abbricht, weil sich Objekte schneiden.
Größere Häuser können dennoch gelegentlich ineinander spawnen :S
Dein Code bricht nie ab. Die foreach bewirkt genau nichts.
Das continue wirkt sich lediglich auf deine foreach aus.
private void SpawnObjects(GameObject planet, GameObject[] prefabs, float objectCount, float distanceToPlanet)
{
List<Bounds> objectsBoundsList = new List<Bounds>();
for (int i = 0; i < objectCount; i++)
{
GameObject spawnObject = prefabs[Random.Range(0, prefabs.Length)];
Vector3 spawnPos = calculator.CalcPointOnSphere(distanceToPlanet);
Bounds tempSpawnBounds = new Bounds(spawnPos, spawnObject.GetComponent<Renderer>().bounds.size);
bool isColliding = false;
foreach (Bounds objectBounds in objectsBoundsList)
{
if (objectBounds.Intersects(tempSpawnBounds))
{
isColliding =true;
break;
}
}
if(isColliding)
continue;
GameObject objectToSpawn = (GameObject)Instantiate(spawnObject, spawnPos, Quaternion.identity);
objectsBoundsList.Add(new Bounds(objectToSpawn.transform.position, objectToSpawn.GetComponent<Renderer>().bounds.size));
}
}
Alternativ die ganze Foreach durch ein Any(...) ersetzen.
Mit dem Debugger kann man solche Probleme relativ einfach aufspüren, indem man an den entsprechenden Stellen Haltepunkte setzt und den Code Schritt für Schritt durchläuft: [Artikel] Debugger: Wie verwende ich den von Visual Studio?
Weeks of programming can save you hours of planning
Ich weiß ja wie man den Debugger nutzt, ich weiß auch wie man ihn mit Unity nutzt.
Dennoch grübel ich über dem Thema, wieso manche Häuser noch ineinander hängen 😃
Selbst die genannte Codeänderung von t0ms3n lässt den Debugger nur 1x abbrechen, ich denke das sollte häufiger passieren.
Naja, ohne Debugger kann man viel vermuten, aber nur schlecht herausfinden, woran es wirklich liegt.
Es gibt Möglichkeiten Unity-Games bzw. -Skripte zu debuggen, such einfach mal bei der Suchmaschine deiner Wahl danach.
Ich kenne mich damit nicht besonders gut aus, aber mir erscheint die Verwendung der BoundingBoxen der Objekte etwas merkwürdig: new Bounds(objectToSpawn.transform.position, objectToSpawn.GetComponent<Renderer>().bounds.size)
.
Eigentlich würde man erwarten, daß ein Objekt eine Eigenschaft mit der bereits transfomierten BoundingBox hat.
Weeks of programming can save you hours of planning
Die 2. Möglichkeit anstatt des Renderers wäre der Collider.
Aber da die Modelle ja aus Einzelteilen bestehen ist es schwierig dem leeren Gameobject als Container einen Collider zu geben