Skip to content

Commit 5de0727

Browse files
committed
feat: basic hex support for Tiled .tmx, closes #702
1 parent 901da1b commit 5de0727

4 files changed

Lines changed: 101 additions & 34 deletions

File tree

fxgl-entity/src/main/kotlin/com/almasb/fxgl/entity/level/tiled/TilesetLoader.kt

Lines changed: 38 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -179,15 +179,14 @@ class TilesetLoader(private val map: TiledMap, private val mapURL: URL) {
179179
return ImageView(buffer)
180180
}
181181

182-
// NOT FULLY IMPLEMENTED, see https://github.com/AlmasB/FXGL/issues/702
183182
fun loadViewHex(layerName: String): Node {
184183
log.debug("Loading view for layer $layerName")
185184

186185
val layer = map.getLayerByName(layerName)
187186

188187
val bufferBottom = WritableImage(
189-
layer.width * map.tilewidth + 100,
190-
layer.height * map.tileheight + 100
188+
layer.width * map.tilewidth + map.tilewidth,
189+
layer.height * map.tileheight + map.tileheight
191190
)
192191

193192
log.debug("Created buffer with size ${bufferBottom.width}x${bufferBottom.height}")
@@ -214,6 +213,7 @@ class TilesetLoader(private val map: TiledMap, private val mapURL: URL) {
214213
val y = i / layer.width
215214

216215
val isColumnEven = x % 2 == 0
216+
val isRowEven = y % 2 == 0
217217

218218
val w = tileset.tilewidth
219219
val h = tileset.tileheight
@@ -243,20 +243,31 @@ class TilesetLoader(private val map: TiledMap, private val mapURL: URL) {
243243
srcy = 0
244244
}
245245

246-
log.debug("Writing to buffer: dst=${x * map.tilewidth},${y * map.tileheight}, w=$w,h=$h, src=$srcx,$srcy")
246+
// map.tilew/h * 0.25 based on https://www.redblobgames.com/grids/hexagons/
247+
var offsetX: Int = if (map.staggeraxis == "x" && x > 0) (map.tilewidth * 0.25).toInt() * -x else 0
248+
var offsetY: Int = if (map.staggeraxis == "y" && y > 0) (map.tileheight * 0.25).toInt() * -y else 0
247249

248-
val offsetX = -(map.hexsidelength + 6) * x
249-
250-
val offsetY = if (map.staggerindex == "even" && isColumnEven
251-
|| map.staggerindex == "odd" && !isColumnEven)
252-
map.tileheight / 2
253-
else
254-
0
255-
256-
// buffer.pixelWriter.setPixels(x * map.tilewidth + offsetX, y * map.tileheight + offsetY,
257-
// w, h, sourceImage.pixelReader,
258-
// srcx,
259-
// srcy)
250+
if (map.staggeraxis == "y") {
251+
if (map.staggerindex == "odd") {
252+
if (!isRowEven) {
253+
offsetX = map.tilewidth / 2
254+
}
255+
} else {
256+
if (isRowEven) {
257+
offsetX = map.tilewidth / 2
258+
}
259+
}
260+
} else {
261+
if (map.staggerindex == "odd") {
262+
if (!isColumnEven) {
263+
offsetY += map.tileheight / 2
264+
}
265+
} else {
266+
if (isColumnEven) {
267+
offsetY += map.tileheight / 2
268+
}
269+
}
270+
}
260271

261272
if (y > oldY) {
262273
delayedDrawings.forEach { it.run() }
@@ -265,9 +276,7 @@ class TilesetLoader(private val map: TiledMap, private val mapURL: URL) {
265276
oldY = y
266277
}
267278

268-
if (map.staggerindex == "odd" && isColumnEven
269-
|| map.staggerindex == "even" && !isColumnEven) {
270-
279+
val drawToBuffer = Runnable {
271280
for (dy in 0 until h) {
272281
for (dx in 0 until w) {
273282
val c = sourceImage.pixelReader.getColor(srcx + dx, srcy + dy)
@@ -281,27 +290,22 @@ class TilesetLoader(private val map: TiledMap, private val mapURL: URL) {
281290
}
282291
}
283292
}
293+
}
294+
295+
if (map.staggerindex == "odd" && isColumnEven
296+
|| map.staggerindex == "even" && !isColumnEven) {
297+
298+
drawToBuffer.run()
284299
} else {
285300

286301
// we need delayed drawings to correctly draw shadows
287-
delayedDrawings += Runnable {
288-
for (dy in 0 until h) {
289-
for (dx in 0 until w) {
290-
val c = sourceImage.pixelReader.getColor(srcx + dx, srcy + dy)
291-
292-
if (c != Color.TRANSPARENT) {
293-
bufferBottom.pixelWriter.setColor(
294-
x * map.tilewidth + offsetX + dx,
295-
y * map.tileheight + offsetY + dy,
296-
c
297-
)
298-
}
299-
}
300-
}
301-
}
302+
delayedDrawings += drawToBuffer
302303
}
303304
}
304305

306+
delayedDrawings.forEach { it.run() }
307+
delayedDrawings.clear()
308+
305309
return ImageView(bufferBottom)
306310
}
307311

fxgl-entity/src/test/kotlin/com/almasb/fxgl/entity/level/tiled/TMXLevelLoaderTest.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,15 @@ class TMXLevelLoaderTest {
198198
assertThat(level.entities.size, `is`(2))
199199
}
200200

201+
@Test
202+
fun `Load hexagonal tmx level`() {
203+
val world = GameWorld()
204+
205+
val level = TMXLevelLoader().load(javaClass.getResource("hex/simple.tmx"), world)
206+
207+
assertThat(level.entities.size, `is`(2))
208+
}
209+
201210
@ParameterizedTest
202211
@CsvSource("sewers_v1_1_2.tmx", "sewers_v1_2_3.tmx", "sewers_v1_9_0.tmx")
203212
fun parse(mapName: String) {
5.65 KB
Loading
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<map version="1.9" tiledversion="1.9.0" orientation="hexagonal" renderorder="right-down" width="20" height="20" tilewidth="14" tileheight="12" infinite="0" hexsidelength="6" staggeraxis="y" staggerindex="odd" nextlayerid="3" nextobjectid="2">
3+
<tileset firstgid="1" name="hex mini" tilewidth="18" tileheight="18" tilecount="20" columns="5">
4+
<image source="hexmini.png" width="106" height="72"/>
5+
</tileset>
6+
<layer id="1" name="Ground" width="20" height="20">
7+
<data encoding="csv">
8+
15,15,15,5,16,17,17,13,8,14,14,14,14,5,13,13,13,13,14,14,
9+
15,8,5,14,16,17,13,8,14,14,14,14,14,5,5,5,13,14,14,14,
10+
15,9,5,2,2,2,2,13,14,14,14,14,14,5,5,5,13,13,14,14,
11+
5,5,2,15,5,7,5,14,14,14,14,14,2,2,5,13,5,13,14,14,
12+
10,5,2,14,14,7,7,14,14,14,13,5,5,2,2,2,2,13,14,14,
13+
10,10,2,2,14,0,0,14,0,10,13,5,2,2,2,2,13,13,14,14,
14+
5,10,10,13,0,0,0,0,0,0,13,5,2,2,2,2,2,13,14,14,
15+
5,13,13,14,14,0,0,0,0,0,0,0,11,2,2,2,2,13,14,10,
16+
5,5,5,14,0,0,0,0,0,0,0,0,11,11,0,0,2,2,10,10,
17+
14,14,14,14,0,0,0,0,0,0,0,0,0,0,0,2,2,10,9,9,
18+
14,14,14,14,14,0,0,0,0,0,0,0,0,0,0,2,2,10,10,9,
19+
14,14,13,13,14,0,0,0,0,0,0,0,0,0,2,12,13,10,10,9,
20+
14,14,13,13,14,14,0,0,0,0,0,0,0,0,0,7,7,13,13,10,
21+
14,13,13,13,14,0,0,0,0,0,0,0,0,0,7,7,13,17,16,10,
22+
14,10,13,13,14,14,0,0,0,0,0,2,2,2,7,7,13,17,17,16,
23+
10,10,13,13,14,14,5,2,2,2,2,4,2,2,2,2,13,16,16,16,
24+
10,10,10,10,13,14,2,2,2,2,4,4,2,7,7,2,13,13,3,16,
25+
10,10,10,14,14,14,5,5,2,4,4,2,12,7,2,3,3,3,3,3,
26+
10,10,14,14,8,14,8,13,5,2,4,4,2,2,2,3,3,3,3,3,
27+
10,14,8,8,8,14,8,13,5,2,2,2,2,3,3,3,3,3,3,3
28+
</data>
29+
</layer>
30+
<layer id="2" name="Layer2" width="20" height="20">
31+
<data encoding="csv">
32+
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
33+
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
34+
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
35+
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
36+
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
37+
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
38+
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
39+
0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,0,0,0,0,0,
40+
0,0,0,0,0,0,0,0,15,15,15,0,0,15,15,0,0,0,0,0,
41+
0,0,0,0,0,0,0,15,15,15,15,15,15,0,0,0,0,0,0,0,
42+
0,0,0,0,0,0,0,15,15,15,15,15,15,0,0,0,0,0,0,0,
43+
0,0,0,0,0,0,0,15,15,15,15,15,0,0,0,0,0,0,0,0,
44+
0,0,0,0,0,0,0,0,15,15,15,0,0,0,0,0,0,0,0,0,
45+
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
46+
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
47+
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
48+
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
49+
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
50+
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
51+
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
52+
</data>
53+
</layer>
54+
</map>

0 commit comments

Comments
 (0)