diff --git a/fxgl-entity/src/main/kotlin/com/almasb/fxgl/entity/level/tiled/TilesetLoader.kt b/fxgl-entity/src/main/kotlin/com/almasb/fxgl/entity/level/tiled/TilesetLoader.kt index 31f2f8d5a..e94db5580 100644 --- a/fxgl-entity/src/main/kotlin/com/almasb/fxgl/entity/level/tiled/TilesetLoader.kt +++ b/fxgl-entity/src/main/kotlin/com/almasb/fxgl/entity/level/tiled/TilesetLoader.kt @@ -305,15 +305,24 @@ class TilesetLoader(private val map: TiledMap, private val mapURL: URL) { return ImageView(bufferBottom) } + /** + * Loads the layer using an isometric projection: + * buffer x = (x-y) * map.tilewidth / 2 + * buffer y = (x+y) * map.tileheight / 2 + * + * in the first case above, we use (maxY - y) instead of just y because we flip the y axis, + * where maxY = layer.height - 1 + */ fun loadViewIsometric(layerName: String): Node { - log.warning("Isometric maps are not fully implemented: https://github.com/AlmasB/FXGL/issues/1151") log.debug("Loading isometric view for layer $layerName") val layer = map.getLayerByName(layerName) + // from the formula above, we calculate maximal dimensions of the buffer + // +1 to handle rounding errors due to integer division val buffer = WritableImage( - layer.width * map.tilewidth, - layer.height * map.tileheight + (layer.width + layer.height) * (map.tilewidth / 2 + 1), + (layer.width + layer.height) * (map.tileheight / 2 + 1) ) log.debug("Created buffer with size ${buffer.width}x${buffer.height}") @@ -364,10 +373,24 @@ class TilesetLoader(private val map: TiledMap, private val mapURL: URL) { srcy = 0 } - buffer.pixelWriter.setPixels(x * map.tilewidth, y * map.tileheight, - w, h, sourceImage.pixelReader, - srcx, - srcy) + val bufferX = (x + map.height-1 - y) * map.tilewidth / 2 + val bufferY = (x + y) * map.tileheight / 2 + + // pixelWriter.setPixels replaces pixels, does not blend them + // in order to take into account transparency, we have to draw pixels 1 by 1 + for (dy in 0 until h) { + for (dx in 0 until w) { + val c = sourceImage.pixelReader.getColor(srcx + dx, srcy + dy) + + if (c != Color.TRANSPARENT) { + buffer.pixelWriter.setColor( + bufferX + dx, + bufferY + dy, + c + ) + } + } + } } return ImageView(buffer) diff --git a/fxgl-entity/src/test/kotlin/com/almasb/fxgl/entity/level/tiled/TMXLevelLoaderTest.kt b/fxgl-entity/src/test/kotlin/com/almasb/fxgl/entity/level/tiled/TMXLevelLoaderTest.kt index b51efff4c..34b054600 100644 --- a/fxgl-entity/src/test/kotlin/com/almasb/fxgl/entity/level/tiled/TMXLevelLoaderTest.kt +++ b/fxgl-entity/src/test/kotlin/com/almasb/fxgl/entity/level/tiled/TMXLevelLoaderTest.kt @@ -189,6 +189,15 @@ class TMXLevelLoaderTest { assertThat(view.image.pixelReader.getColor(31,16), `is`((Color.TRANSPARENT))) } + @Test + fun `Load isometric tmx level`() { + val world = GameWorld() + + val level = TMXLevelLoader().load(javaClass.getResource("iso/simple.tmx"), world) + + assertThat(level.entities.size, `is`(2)) + } + @ParameterizedTest @CsvSource("sewers_v1_1_2.tmx", "sewers_v1_2_3.tmx", "sewers_v1_9_0.tmx") fun parse(mapName: String) { diff --git a/fxgl-entity/src/test/resources/com/almasb/fxgl/entity/level/tiled/iso/barrel.png b/fxgl-entity/src/test/resources/com/almasb/fxgl/entity/level/tiled/iso/barrel.png new file mode 100644 index 000000000..ab0df938a Binary files /dev/null and b/fxgl-entity/src/test/resources/com/almasb/fxgl/entity/level/tiled/iso/barrel.png differ diff --git a/fxgl-entity/src/test/resources/com/almasb/fxgl/entity/level/tiled/iso/simple.tmx b/fxgl-entity/src/test/resources/com/almasb/fxgl/entity/level/tiled/iso/simple.tmx new file mode 100644 index 000000000..dc915f322 --- /dev/null +++ b/fxgl-entity/src/test/resources/com/almasb/fxgl/entity/level/tiled/iso/simple.tmx @@ -0,0 +1,49 @@ + + + + + + + + + + +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, +1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1, +1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1, +1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1, +1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1, +1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1, +1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1, +1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1, +1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1, +1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1, +1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1, +1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1, +1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1, +1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,1,1,1, +1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 + + + + +0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,2,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,2,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + + + diff --git a/fxgl-entity/src/test/resources/com/almasb/fxgl/entity/level/tiled/iso/tile1.png b/fxgl-entity/src/test/resources/com/almasb/fxgl/entity/level/tiled/iso/tile1.png new file mode 100644 index 000000000..c7d48eea1 Binary files /dev/null and b/fxgl-entity/src/test/resources/com/almasb/fxgl/entity/level/tiled/iso/tile1.png differ