Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Garzec

Alter Hase

  • »Garzec« ist der Autor dieses Themas

Beiträge: 693

Wohnort: Gießen

  • Private Nachricht senden

1

20.05.2017, 23:47

Conway's Game of Life in Unity - zu richtigen Zeitpunkten Zyklen berechnen

Hi,
ich wollte mich mal an Conway's Game of Life setzen. Finde die Simulation sehr interessant.

Nun bin ich soweit, dass es eigentlich fertig ist, aber noch Probleme auftauchen könnten. Ich habe die Zellen, bestimme, ob sie leben oder tot sind und kann neue durch Klicken zum Leben erwecken. Wenn der Zyklus aber normal abläuft, könnte es ja passieren, dass die Zellen sich gegenseitig prüfen und es zu Unstimmigkeiten kommt, wenn eine Zelle stirbt und die vorherig prüfende davon natürlich noch nichts weiß.

Im Netz stand etwas davon, ein zweites Grid anzulegen, wo man dann sämtliche Züge der Zellen ablegt und hinterher gezielt wieder auf das erste Grid überträgt. Wie macht man dann sowas?

Meine Gridverwaltung ist recht überschaubar (ist dann eigentlich auch das komplette Programm, außer der Klasse für die Zelle mit ein paar Eigenschaften drin),

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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
    [SerializeField]
    GameObject cellObject;
    
    private Vector2 mapSize;

    private List<GameObject> cells = new List<GameObject>();

    public float countTime { get; set; }

    public float cycleInterval { get { return 2; } }

    private void Start()
    {
        mapSize = new Vector2(10, 10); // 100 Zellen zum Testen
    
        for (int x = 0; x < mapSize.x; x++)
        {
            for (int y = 0; y < mapSize.y; y++)
            {
                GameObject cell = Instantiate(cellObject, new Vector2(x, y), Quaternion.identity); // Map erzeugen und alle Zellen in die Liste packen
                cells.Add(cell);
            }
        }
    }

    private void Update()
    {
        countTime += Time.deltaTime;
        if (countTime >= cycleInterval)
        {
            NextCycle();
            countTime = 0;
        }
    }

    private void NextCycle() // neue Folgegeneration starten
    {
        foreach (GameObject cell in cells) // Für alle Zellen durchführen
        {
            CellController c = cell.GetComponent<CellController>();

            bool isAlive = c.GetState(); // Lebt die Zelle?

            Vector2 cellPositon = c.GetCellPosition();
            int x = (int)cellPositon.x;
            int y = (int)cellPositon.y;

            int neighbourCount = GetNeighbourCount(x, y); // Anzahl aller lebendigen Nachbarszellen

            bool result = false;

            if (isAlive && neighbourCount < 2)
                result = false;
            if (isAlive && (neighbourCount == 2 || neighbourCount == 3))
                result = true;
            if (isAlive && neighbourCount > 3)
                result = false;
            if (!isAlive && neighbourCount == 3)
                result = true;

            c.SetState(result); // Zelle lebendig setzen oder sterben lassen
        }
    }

    private int GetNeighbourCount(int x, int y) // Alle Nachbarn der Zelle prüfen
    {
        int count = 0;

        if (x != mapSize.x - 1)
            if (GetNeighbourCell(x + 1, y).GetComponent<CellController>().GetState())
                count++;

        if (x != mapSize.x - 1 && y != mapSize.y - 1)
            if (GetNeighbourCell(x + 1, y + 1).GetComponent<CellController>().GetState())
                count++;

        if (y != mapSize.y - 1)
            if (GetNeighbourCell(x, y + 1).GetComponent<CellController>().GetState())
                count++;

        if (x != 0 && y != mapSize.y - 1)
            if (GetNeighbourCell(x - 1, y + 1).GetComponent<CellController>().GetState())
                count++;

        if (x != 0)
            if (GetNeighbourCell(x - 1, y).GetComponent<CellController>().GetState())
                count++;

        if (x != 0 && y != 0)
            if (GetNeighbourCell(x - 1, y - 1).GetComponent<CellController>().GetState())
                count++;

        if (y != 0)
            if (GetNeighbourCell(x, y - 1).GetComponent<CellController>().GetState())
                count++;

        if (x != mapSize.x - 1 && y != 0)
            if (GetNeighbourCell(x + 1, y - 1).GetComponent<CellController>().GetState())
                count++;

        return count;
    }

    private GameObject GetNeighbourCell(int x, int y) // Zelle anhand der Koordinaten suchen
    {
        GameObject cell = null;

        foreach (GameObject c in cells)
        {
            if (c.GetComponent<CellController>().GetCellPosition() == new Vector2(x, y))
            {
                cell = c;
                break;
            }
        }

        return cell;
    }


Ich bin mir auch ziemlich sicher, dass da noch was für die Performance getan werden kann. So viele "GetComponents" lassen mich dann doch schonmal nachdenklich werden. Aber erstmal wollte ich die möglicherweise auftretenden Fehler beseitigen.

Wäre cool, wenn mir jemand den Trick mit dem 2. Grid verraten kann :)

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

2

21.05.2017, 00:28

Du darfst den Zustand der Zellen nicht sofort verändern, sondern musst den zukünftigen Zustand jeder Zelle berechnen und zwischenspeichern (das ist quasi das zweite Grid). Erst wenn du das für alle Zellen getan hast, überträgst du für jede Zelle den zukünftigen Zustand auf den aktuellen Zustand.

Garzec

Alter Hase

  • »Garzec« ist der Autor dieses Themas

Beiträge: 693

Wohnort: Gießen

  • Private Nachricht senden

3

21.05.2017, 08:01

Danke :)

Renegade

Alter Hase

Beiträge: 494

Wohnort: Berlin

Beruf: Certified Unity Developer

  • Private Nachricht senden

4

21.05.2017, 12:39

Für dieses Problem könnte man einen Double Buffer verwenden, wie ihn Bob Nystrom in seinem Beispiel für Slapstick Comedy Beispiel beschreibt.
Liebe Grüße,
René

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

5

21.05.2017, 13:08

Für dieses Problem könnte man einen Double Buffer verwenden, wie ihn Bob Nystrom in seinem Beispiel für Slapstick Comedy Beispiel beschreibt.

Das ist doch genau das, was ich ihm vorgeschlagen habe.

Renegade

Alter Hase

Beiträge: 494

Wohnort: Berlin

Beruf: Certified Unity Developer

  • Private Nachricht senden

6

21.05.2017, 14:40


Das ist doch genau das, was ich ihm vorgeschlagen habe.

Widerspreche ich auch nicht. Ich möchte ihm nur einen Namen für ein mögliches Verfahren und eine beispielhafte Implementierung an die Hand geben. Sieh es als Ergänzung und nicht als Kritik deiner Aussage. Vielleicht hilft es auch weiteren geneigten Lesern bei ihren Problemen.

@Garzec: Du solltest bereits beim Erstellen der Zellen die jeweilige Cell in die Liste speichern, anstatt des GameObjects. Lege dir dafür einfach eine Cell an, die im Konstruktor das zugehörige GameObject referenziert (und als Property zurück gibt), dann ersparst du dir die unnötigen GetComponents. Die GetNeighbourCount kannst du refaktorieren, da sich lediglich die Argumente und die Bedingungen unterscheiden.
Liebe Grüße,
René

Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von »Renegade« (21.05.2017, 15:00)


Werbeanzeige