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

1

03.05.2014, 19:45

(2d)Chunksystem

Hey Leute,
ich arbeite gerade an einem 2D-Spiel, in dem man sich als Spieler über eine sehr große Karte bewegen kann. Aufgrund ihrer Größe kann ich sie nicht vollständig im Arbeitsspeicher speichern, muss sie also in Teilen(als .png.-Bilder) von der Festplatte laden. Damit das Spiel beim Laden nicht ruckelt, lade ich das Bild in einem nebenläufigen Thread. Doch leider habe ich sehr viele Schwierigkeiten bei der Synchronisation, und auch beim Algorithmus, der entscheidet welche Chunks geladen werden müssen selber. Könntet ihr mir vielleicht einige Tipps, oder sogar Code, zu dem Thema geben ? Wenn ihr mehr Infos wollt fragt einfach.
Vielen Dank im Voraus, mit freundlichen Grüßen, Playking

birdfreeyahoo

Alter Hase

Beiträge: 756

Wohnort: Schorndorf

Beruf: Junior Software Engineer

  • Private Nachricht senden

2

03.05.2014, 20:06

Ich hatte sowas mal für eine 3D-Welt (Minecraft-like) programmiert, befürchte aber dass ich den quellcode gelöscht habe.

TrommlBomml

Community-Fossil

Beiträge: 2 117

Wohnort: Berlin

Beruf: Software-Entwickler

  • Private Nachricht senden

3

03.05.2014, 20:11

Das ermitteln der zu ladenden Chunks sollte doch relativ simpel sein: Alle Chunks, die genau um die aktuell sichtbaren drum herum liegen. Ausserdem sollte das auch ziemlich fix gehen, wenn deine Texturen gecached sind.

Synchronisiert werden sollte wohl nur der Zugriff auf die Chunk-Liste. Entweder der Nachlade-Thread kann gerade ein Objekt in die aktive Chunkliste hineintun, oder der Renderthread sie durchiterieren und rendern.

NACHTRAG: Wahrscheinlich kannst du die Synchronisationszeit noch stark reduzieren, wenn im Renderthread eine Kopie der Chunkliste threadsicher erzeugt wird, danach normal gerendert wird, dann kann während des Renderns die Originalliste durch den Hintergrundthread aktualisiert werden und im nächsten Render-aufruf ist die Liste aktuell, was pro Frame sicherstellt, dass die Listen immer aktuelle sind.

4

03.05.2014, 20:19

Danke, deine Tipps klingen sehr gut. Werde morgen mal probieren ob ich das umsetzen kann...
Nachtrag: Mit 6400 * 6400 Pixeln sind meine Chunks auch ein bisschen groß oder ?

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »playking21« (03.05.2014, 20:46)


BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

5

03.05.2014, 23:00

Auf UHD-Geräten nicht. Auf allen anderen aber vermutlich schon, je nachdem wie schnell der Spieler hindurch stürmt oder eben nicht.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

6

04.05.2014, 10:36

Sorry, habe echt Schwierigkeiten die Tipps zu befolgen. Ich stelle jetzt einfach mal den Sourcecode hier rein, vielleicht könnt ihr mir ja anhand des Sourcecodes ein paar Tipps geben.
Code:

C-/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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
Vector2f temppos (Player::sprite.getPosition());
//Punkte an denen ich prüfe, ob ein Chunk geladen ist
    Vector2<int> pos1 (temppos.x - 1000, temppos.y - 1000);
    Vector2<int> pos2 (temppos.x + 1000, temppos.y - 1000);
    Vector2<int> pos3 (temppos.x + 1000, temppos.y + 1000);
    Vector2<int> pos4 (temppos.x - 1000, temppos.y + 1000);
//gibt an, ob an der Position ein Chunk ist
    bool bpos1 = false, bpos2 = false, bpos3 = false, bpos4 = false;
    //gibt an ob ein Chunk geladen werden muss
    bool createThread = false;
    //Chunk.used gibt an, ob der Chunk gerendert wird, oder "übrig ist"
    chunk1.used = false;
    chunk2.used = false;
    chunk3.used = false;
    chunk4.used = false;
//überprüfe pos1
    if(pos1.x >= (rect1.left << 6) && pos1.x <= (rect1.left << 6) + (rect1.width << 6) && pos1.y >= (rect1.top << 6) && pos1.y <= (rect1.top << 6) + (rect1.height << 6)) {
chunk1.used = true;
bpos1 = true;
    } else if(pos1.x >= (rect2.left << 6) && pos1.x <= (rect2.left << 6) + (rect2.width << 6) && pos1.y >= (rect2.top << 6) && pos1.y <= (rect2.top << 6) + (rect2.height << 6)) {
chunk2.used = true;
bpos1 = true;
    } else if(pos1.x >= rect3.left * 64 && pos1.x <= rect3.left * 64 + rect3.width * 64 && pos1.y >= rect3.top * 64 && pos1.y <= rect3.top * 64 + rect3.height * 64) {
chunk3.used = true;
bpos1 = true;
    } else if(pos1.x >= rect4.left * 64 && pos1.x <= rect4.left * 64 + rect4.width * 64 && pos1.y >= rect4.top * 64 && pos1.y <= rect4.top * 64 + rect4.height * 64) {
chunk4.used = true;
bpos1 = true;
    }

    //überprüfe pos2
if(pos2.x >= rect1.left * 64 && pos2.x <= rect1.left * 64 + rect1.width * 64 && pos2.y >= rect1.top * 64 && pos2.y <= rect1.top * 64 + rect1.height * 64) {
chunk1.used = true;
bpos2 = true;
} else if(pos2.x >= rect2.left * 64 && pos2.x <= rect2.left * 64 + rect2.width * 64 && pos2.y >= rect2.top * 64 && pos2.y <= rect2.top * 64 + rect2.height * 64) {
chunk2.used = true;
bpos2 = true;
} else if(pos2.x >= rect3.left * 64 && pos2.x <= rect3.left * 64 + rect3.width * 64 && pos2.y >= rect3.top * 64 && pos2.y <= rect3.top * 64 + rect3.height * 64) {
chunk3.used = true;
bpos2 = true;
} else if(pos2.x >= rect4.left * 64 && pos2.x <= rect4.left * 64 + rect4.width * 64 && pos2.y >= rect4.top * 64 && pos2.y <= rect4.top * 64 + rect4.height * 64) {
chunk4.used = true;
bpos2 = true;
}

    //überprüfe pos3
if(pos3.x >= rect1.left * 64 && pos3.x <= rect1.left * 64 + rect1.width * 64 && pos3.y >= rect1.top * 64 && pos3.y <= rect1.top * 64 + rect1.height * 64) {
chunk1.used = true;
bpos3 = true;
} else if(pos3.x >= rect2.left * 64 && pos3.x <= rect2.left * 64 + rect2.width * 64 && pos3.y >= rect2.top * 64 && pos3.y <= rect2.top * 64 + rect2.height * 64) {
chunk2.used = true;
bpos3 = true;
} else if(pos3.x >= rect3.left * 64 && pos3.x <= rect3.left * 64 + rect3.width * 64 && pos3.y >= rect3.top * 64 && pos3.y <= rect3.top * 64 + rect3.height * 64) {
chunk3.used = true;
bpos3 = true;
} else if(pos3.x >= rect4.left * 64 && pos3.x <= rect4.left * 64 + rect4.width * 64 && pos3.y >= rect4.top * 64 && pos3.y <= rect4.top * 64 + rect4.height * 64) {
chunk4.used = true;
bpos3 = true;
}

    //überprüfe pos4
if(pos4.x >= rect1.left * 64 && pos4.x <= rect1.left * 64 + rect1.width * 64 && pos4.y >= rect1.top * 64 && pos4.y <= rect1.top * 64 + rect1.height * 64) {
chunk1.used = true;
bpos4 = true;
} else if(pos4.x >= rect2.left * 64 && pos4.x <= rect2.left * 64 + rect2.width * 64 && pos4.y >= rect2.top * 64 && pos4.y <= rect2.top * 64 + rect2.height * 64) {
chunk2.used = true;
bpos4 = true;
} else if(pos4.x >= rect3.left * 64 && pos4.x <= rect3.left * 64 + rect3.width * 64 && pos4.y >= rect3.top * 64 && pos4.y <= rect3.top * 64 + rect3.height * 64) {
chunk3.used = true;
bpos4 = true;
} else if(pos4.x >= rect4.left * 64 && pos4.x <= rect4.left * 64 + rect4.width * 64 && pos4.y >= rect4.top * 64 && pos4.y <= rect4.top * 64 + rect4.height * 64) {
chunk4.used = true;
bpos4 = true;
}

    while(true) {
//Chunk& loadInChunk, ist eine Referenz auf den Chunk in den gekaden wird.
if(!chunk1.used) {
loadInChunk = chunk1;
} else if(!chunk2.used) {
loadInChunk = chunk2;
} else if(!chunk3.used) {
loadInChunk = chunk3;
} else if(!chunk4.used) {
loadInChunk = chunk4;
} else {
loadInChunk = empty;
}
//sf::Vector2f loadpos, ist die Position an der geladen wird.
if(!loadposmutex) {
loadposmutex = true;
if(!bpos1) {
if(pos1.x >= 0 && pos1.y >= 0) {
loadpos.x = pos1.x;
loadpos.y = pos1.y;
bpos1 = true;
createThread = true;
} else {
bpos1 = true;
}
} else if(!bpos2) {
if(pos2.x >= 0 && pos2.y >= 0) {
loadpos.x = pos2.x;
loadpos.y = pos2.y;
bpos2 = true;
createThread = true;
} else {
bpos2 = true;
}
} else if(!bpos3) {
if(pos3.x >= 0 && pos3.y >= 0) {
loadpos.x = pos3.x;
loadpos.y = pos3.y;
bpos3 = true;
createThread = true;
} else {
bpos3 = true;
}
} else if(!bpos4) {
if(pos4.x >= 0 && pos4.y >= 0) {
loadpos.x = pos4.x;
loadpos.y = pos4.y;
bpos4 = true;
createThread = true;
} else {
bpos4 = true;
}
}
loadposmutex = false;
}
//Von Pixel zu Chunks umrechnen
loadpos.x = loadpos.x - loadpos.x%6400;
loadpos.y = loadpos.y - loadpos.y%6400;
loadpos.x /= 6400;
loadpos.y /= 6400;
if(createThread) {
Clock clock;
while(threadmutex) {

}
//thread erstellen
threadmutex = true;
std::thread t (&World::loadChunk, this);
//t.detach(); //sollte t.detach() sein, das synchronisieren funktioniert aber nicht, wenn ich im Compiler das optimieren einstelle
t.join();
createThread = false;
}
//Wenn alle Chunks geladen sind
if(bpos1 && bpos2 && bpos3 && bpos4)break;
}

void World::loadChunk() {
    loadInChunk.used = true;
    loadInChunk.loaded = false;
//Die sf::IntRect rect1-rect4 geben an, welche Stelle von dem Chunk abgedeckt wird(In der Einheit 64 Pixel).
    if(loadInChunk.chunkid == 0) {
rect1.left = loadpos.x * 100;
rect1.top = loadpos.y * 100;
rect1.height = 100;
rect1.width = 100;
    } else if(loadInChunk.chunkid == 1) {
rect2.left = loadpos.x * 100;
rect2.top = loadpos.y * 100;
rect2.height = 100;
rect2.width = 100;
    } else if(loadInChunk.chunkid == 2) {
rect3.left = loadpos.x * 100;
rect3.top = loadpos.y * 100;
rect3.height = 100;
rect3.width = 100;
    } else if(loadInChunk.chunkid == 3) {
rect4.left = loadpos.x * 100;
rect4.top = loadpos.y * 100;
rect4.height = 100;
rect4.width = 100;
    }
    if(loadpos.x == 0) {
if(loadpos.y == 0 || loadpos.y == 1) {
Texture* mtex = &world_texch1;
loadInChunk.used = true;
if(loadInChunk.chunkid == 1) {
mtex = &world_texch2;
} else if(loadInChunk.chunkid == 2) {
mtex = &world_texch3;
} else if(loadInChunk.chunkid == 3) {
mtex = &world_texch4; 
}
while(true) {
if(!loadInChunk.mutex) {
loadInChunk.mutex = true;
break;
} else sleep(sf::milliseconds(20));
}
std::stringstream ss1;
ss1 << loadpos.x;
std::stringstream ss2;
ss2 << loadpos.y;
mtex -> loadFromFile(RESPATH + ss1.str() + "_" + ss2.str() + ".png");
loadTiles(loadInChunk.chunkid, *mtex, loadpos.x * 6400, loadpos.y * 6400); 
loadInChunk.loaded = true;  
}
    }
    loadInChunk.mutex = false;
    threadmutex = false;
}

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »playking21« (04.05.2014, 14:21)


TrommlBomml

Community-Fossil

Beiträge: 2 117

Wohnort: Berlin

Beruf: Software-Entwickler

  • Private Nachricht senden

7

04.05.2014, 12:06

kannst du den code entweder bei nopaste reinstellen oder Code-Tags verwenden? ads kann man sonst nicht wirklich lesen.

Ansonsten wenn du Probleme hast, ist das analysieren mit mehreren Threads gar nicht so einfach. Dann mache lieber erstmal alles in einem Thread, z.B. zuerst Chunks nachladen und dann rendern. Wenn das korrekt funktioniert, kann man alles asynchron machen.

8

04.05.2014, 14:08

In einem Thread funktioniert es. Ist halt nur so, dass ich das mit Threads nicht auf die Reihe bekomme :(

Werbeanzeige

Ähnliche Themen