Du bist nicht angemeldet.

Werbeanzeige

Garzec

Alter Hase

  • »Garzec« ist der Autor dieses Themas

Beiträge: 693

Wohnort: Gießen

  • Private Nachricht senden

1

16.11.2016, 18:37

Aufeinander platzierte Objekte zerstören

Hi,
ich habe für ein Schulprojekt in Unity einen Planeten mit Gravitation gebastelt. Die erstellten Objekte werden random gespawnt und dann durch die Gravitation an die Kugel (Planet) gezogen und ausgerichtet.

Jetzt kann es vorkommen, dass sich Objekte am Spawn schneiden und dann aufeinander gebaut werden. Da möchte ich das obere Objekt einfach zerstören, weil ein Haus auf einem Haus sieht seltsam aus.

Hier das Script:

C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Vector3 planetCenter;
 
     void Start()
     {
         planetCenter = GameObject.FindGameObjectWithTag("Planet").transform.position;
     }
 
     void OnCollisionEnter(Collision other)
     {
         if(other.gameObject.tag == "SpawnObject")
         {
             Vector3 thisPos = transform.position;
             Vector3 otherPos = other.gameObject.transform.position;

             float thisLength = Mathf.Sqrt(Mathf.Pow(thisPos.x - planetCenter.x, 2) +
                 Mathf.Pow(thisPos.x - planetCenter.x, 2) + Mathf.Pow(thisPos.x - planetCenter.x, 2));

             float otherLength = Mathf.Sqrt(Mathf.Pow(otherPos.x - planetCenter.x, 2) +
                 Mathf.Pow(otherPos.x - planetCenter.x, 2) + Mathf.Pow(otherPos.x - planetCenter.x, 2));
 
             if (thisLength<otherLength)
             {
                 Destroy(gameObject);
             }
             else
             {
                 Destroy(other.gameObject);
             }
         }
     }


Nun möchte ich, dass das Script disabled wird, sobald alle Objekte auf dem Planeten "gelandet" sind, damit ein lebendiges Objekt bei Kollision nicht das andere Objekt zerstören kann.

Ich könnte auch den "toten" Objekten einen anderen Tag zuweisen und das Script dann nur auf die Kollision von Haus zu Haus prüfen lassen, aber ich wollte mal fragen, ob man sowas eleganter lösen kann.

Eine gewisse Zeit lang mit der Deaktivierung zu warten wäre ja fatal, je nachdem wie lange mein alter Laptop die Welt bauen muss ergeben sich ja andere Ergebnisse...

Schorsch

Supermoderator

Beiträge: 5 114

Wohnort: Wickede

Beruf: Softwareentwickler

  • Private Nachricht senden

2

16.11.2016, 18:41

Speicher die Objekte die erzeugt wurden in einer extra Liste. Wurden alle Objekte erzeugt durchläufst du diese Liste und deaktivierst die Skripte.
„Es ist doch so. Zwei und zwei macht irgendwas, und vier und vier macht irgendwas. Leider nicht dasselbe, dann wär's leicht.
Das ist aber auch schon höhere Mathematik.“

Garzec

Alter Hase

  • »Garzec« ist der Autor dieses Themas

Beiträge: 693

Wohnort: Gießen

  • Private Nachricht senden

3

16.11.2016, 19:33

Die Idee hatte ich schon, aber ich weiß noch nicht, ob die Aufrufreihenfolge das mitmacht.

Spawnen tue ich die Objekte so:

C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
float objectCount = 300;

    public void PlaceObjectsOnPlanet(GameObject planet, GameObject[] planetPrefabs)
    {
        List<GameObject> objectList = new List<GameObject>();

        for (int i = 0; i < objectCount; i++)
        {
            float angleWidth = Random.Range(-Mathf.PI, Mathf.PI);
            float angleHeight = Random.Range(-Mathf.PI, Mathf.PI);
            float angleZ = Mathf.Sin(angleWidth);

            Vector3 spawnPos = new Vector3(
                planet.transform.position.x + planet.transform.localScale.x * Mathf.Cos(angleWidth) * Mathf.Cos(angleHeight),
                planet.transform.position.y + planet.transform.localScale.y * Mathf.Cos(angleWidth) * Mathf.Sin(angleHeight),
                planet.transform.position.z + planet.transform.localScale.z * angleZ);

            GameObject spawnedObject = (GameObject)Instantiate(planetPrefabs[Random.Range(0, planetPrefabs.Length)], spawnPos, Quaternion.identity);
            objectList.Add(spawnedObject);
        }
           foreach (GameObject obj in objectList)
            {
                obj.GetComponent<ObjectDestroyer>().enabled = false;
            }
    }


Also die Deaktivierung scheint wohl noch aufgerufen zu werden, bevor geprüft werden kann, ob die Objekte kollidieren.

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Garzec« (16.11.2016, 19:42)


Sacaldur

Community-Fossil

Beiträge: 2 280

Wohnort: Berlin

Beruf: FIAE

  • Private Nachricht senden

4

16.11.2016, 19:56

Im Grunde könntest du noch vor dem Platzieren prüfen, ob das Objekt problemlos platziert werden kann. Von einem Gebäude müssen die anderen Gebäude einen bestimmten Abstand (in Grad) haben. Dieser hängt von dem zu setzenden Gebäude und dem bereits platzierten Gebäude ab. Da du weißt, wie die Platzierung durchgeführt wird, kannst du diese Mindestabstände errechnen, bevor Objekte gespawnt werden, entsprechend kannst du vorher bereits prüfen, ob ein Spawnen notwendig ist.
Und vielleicht hast du das "Komponente deaktivieren" etwas zu wörtlich genommen. Vielleicht meinte Schorsch, dass man anhand der Liste alle gespawnten Objekte durchgehen und prüfen kann, ob diese kollidieren, um sie dann zu zerstören. Aber das ist eher eine Vermutung meinerseits.

Weiterhin: planetCenter = GameObject.FindGameObjectWithTag("Planet").transform.position;
Einerseits solltest du den Planeten per Editor zuweisen, andererseits könnte es sinnvoll sein, die Position nicht einmal am Anfang abzurufen, da so ein Verschieben des Planets nicht mehr möglich ist. Ob letzteres relevant ist, hängt vom Spiel ab.
Spieleentwickler in Berlin? (Thema in diesem Forum)
---
Es ist ja keine Schande etwas falsch zu machen, als Programmierer tu ich das täglich, [...].

Garzec

Alter Hase

  • »Garzec« ist der Autor dieses Themas

Beiträge: 693

Wohnort: Gießen

  • Private Nachricht senden

5

16.11.2016, 20:09

Nein, der Planet ist fix :) Da es ja nur ein kleines Projekt für die Schule ist, wollte ich Berechnungen in der StartMethode lassen, damit ichs nicht vergesse, die Objekte alle reinzuziehen etc.

Wie berechnet man so etwas? Ich würde jetzt das erstellte Objekt in einer Variablen speichern, mir Position und das localScale holen und dann Gegenrechnen, ob die Größen ineinander ragen.

Garzec

Alter Hase

  • »Garzec« ist der Autor dieses Themas

Beiträge: 693

Wohnort: Gießen

  • Private Nachricht senden

6

18.11.2016, 14:39

Also ich habe mal angenommen, ich baue eine imaginäre Kugel um alle Objekte und vergleiche die Distanz der Mittelpunkte und der Radien.

Die Performance ist durch die foreach-Schleife natürlich katastrophal, aber erstmal muss ich versuchen das Ziel zu erreichen, hier der Code:

C#-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
private float objectCount = 100;

    public void PlaceObjectsOnPlanet(GameObject planet, GameObject[] planetPrefabs)
    {
        List<GameObject> spawnList = new List<GameObject>();

        for (int i = 0; i < objectCount; i++)
        {
            float angleWidth = Random.Range(-Mathf.PI, Mathf.PI);
            float angleHeight = Random.Range(-Mathf.PI, Mathf.PI);
            float angleZ = Mathf.Sin(angleWidth);

            Vector3 spawnPos = new Vector3(
                planet.transform.position.x + planet.transform.localScale.x * Mathf.Cos(angleWidth) * Mathf.Cos(angleHeight),
                planet.transform.position.y + planet.transform.localScale.y * Mathf.Cos(angleWidth) * Mathf.Sin(angleHeight),
                planet.transform.position.z + planet.transform.localScale.z * angleZ);

            GameObject objectToSpawn = planetPrefabs[Random.Range(0, planetPrefabs.Length)];

            bool canPlace = true;

            foreach (GameObject listedObject in spawnList)
            {
                if (CheckPlacement(listedObject, spawnPos,
                    objectToSpawn.transform.localScale.x,
                    objectToSpawn.transform.localScale.y,
                    objectToSpawn.transform.localScale.z))
                {
                    canPlace = false;
                }
            }

            if (canPlace)
            {
                Instantiate(objectToSpawn, spawnPos, Quaternion.identity);
                spawnList.Add(objectToSpawn);
            }
        }
    }

    private bool CheckPlacement(GameObject placedObject, Vector3 nextSpawnPos, float nextScaleX, float nextScaleY, float nextScaleZ)
    {
        // Vorhandenes Objekt
        Vector3 placedObjectPos = placedObject.transform.position;
        float placedObjectScaleX = placedObject.transform.localScale.x;
        float placedObjectScaleY = placedObject.transform.localScale.y;
        float placedObjectScaleZ = placedObject.transform.localScale.z;

        // Eckpunkt Objekt A
        Vector3 cornerA = new Vector3(placedObjectPos.x + placedObjectScaleX, 
                                        placedObjectPos.y + placedObjectScaleY, 
                                        placedObjectPos.z + placedObjectScaleZ);

        // Radius Objekt A
        float radiusA = Mathf.Sqrt(Mathf.Pow(cornerA.x - placedObjectPos.x, 2) +
                                    Mathf.Pow(cornerA.y - placedObjectPos.y, 2) +
                                    Mathf.Pow(cornerA.z - placedObjectPos.z, 2));

        // neues Objekt
        Vector3 cornerB = new Vector3(nextSpawnPos.x + nextScaleX,
                                        nextSpawnPos.y + nextScaleY,
                                        nextSpawnPos.z + nextScaleZ);

        // Radius neues Objekt
       float radiusB = Mathf.Sqrt(Mathf.Pow(cornerB.x - nextSpawnPos.x, 2) +
                                    Mathf.Pow(cornerB.y - nextSpawnPos.y, 2) +
                                    Mathf.Pow(cornerB.z - nextSpawnPos.z, 2));

        // Distanz der Mittelpunkte
        float distanceCenterToCenter = Mathf.Sqrt(Mathf.Pow(nextSpawnPos.x - placedObjectPos.x, 2) +
                                    Mathf.Pow(nextSpawnPos.y - placedObjectPos.y, 2) +
                                    Mathf.Pow(nextSpawnPos.z - placedObjectPos.z, 2));

        // Summe der Radien
        float sumRadius = radiusA + radiusB;

        if (distanceCenterToCenter < sumRadius)
        {
            // Objekte berühren sich
            return false;
        }

        return true;
    }


Bei den 100 Objekten werden meistens 2 bis 3 gespawnt. Die Console meint die Mittelpunktdistanzen sind aber größer als die Summe der Radien, ich schätze ich habe oben in der Hauptroutine einen Fehler.

Werbeanzeige