Some tilemap editor improvements

This commit is contained in:
soywiz
2020-01-16 03:05:32 +01:00
parent 0f6778fedc
commit 8f1d83e4e2
4 changed files with 150 additions and 76 deletions

View File

@@ -18,7 +18,7 @@ Download from releases:
Output: `build/distributions/KorgePlugin.zip`
### Testing
### Executing from Source
```
./gradlew runIde
@@ -26,4 +26,10 @@ Output: `build/distributions/KorgePlugin.zip`
This should launch a new intelliJ Community Edition with the KorGE plugin.
You can open this project with IDEA and launch the `runIde` task via debugger
to be able to debug the plugin.
to be able to debug the plugin.
### Trying the Tiled Editor
```
./gradlew runDebugTilemap
```

View File

@@ -1,14 +1,15 @@
package com.soywiz.korge.intellij.editor.tile
import com.intellij.ui.components.*
import com.intellij.uiDesigner.core.*
import com.soywiz.korge.tiled.*
import com.soywiz.korim.awt.*
import com.soywiz.korim.bitmap.*
import com.soywiz.korim.color.*
import com.soywiz.korio.file.std.*
import com.soywiz.korma.geom.*
import kotlinx.coroutines.*
import java.awt.*
import java.awt.image.*
import java.awt.event.*
import javax.swing.*
class MyTileMapEditorPanel(val tmx: TiledMap) : JPanel(BorderLayout()) {
@@ -16,105 +17,153 @@ class MyTileMapEditorPanel(val tmx: TiledMap) : JPanel(BorderLayout()) {
val maxTileGid = tmx.tilesets.map { it.firstgid + it.tileset.textures.size }.max() ?: 0
data class TileInfo(val image: Bitmap32, val awt: BufferedImage, val area: RectangleInt) {
val miniImage = awt.getSubimage(area.x, area.y, area.width, area.height)
val miniBmp32 = image.copySliceWithSize(area.x, area.y, area.width, area.height)
val miniSlice = miniBmp32.sliceWithSize(0, 0, area.width, area.height)
data class TileInfo(val bmp32: Bitmap32) {
val miniSlice = bmp32.slice()
}
private val emptyImage = Bitmaps.transparent.bmp
private val emptyImageAwt = emptyImage.toAwt()
val dummyTile = TileInfo(emptyImage, emptyImageAwt, Bitmaps.transparent.bounds)
val tiles: Array<TileInfo> = Array(maxTileGid) { dummyTile }.also { tiles ->
val tiles = Array<TileInfo?>(maxTileGid) { null }.also { tiles ->
for (tileset in tmx.tilesets) {
val tex = tileset.tileset.base.toBMP32()
val tex2 = tex.toAwt()
for (tileIdx in tileset.tileset.textures.indices) {
val tile = tileset.tileset.textures[tileIdx]
if (tile != null) {
tiles[tileset.firstgid + tileIdx] = TileInfo(tex, tex2, (tile as BitmapSlice<*>).bounds)
val miniSlice = (tile as BitmapSlice<*>).extract().toBMP32()
tiles[tileset.firstgid + tileIdx] = when {
miniSlice.allFixed { it.a == 0 } -> null // Transparent
else -> TileInfo(miniSlice)
}
}
}
}
}
val realPanel = tileMapEditor.contentPanel
var scale = 2.0
set(value) {
field = value
mapComponent.updateSize()
mapComponent.revalidate()
mapComponentScroll.revalidate()
mapComponentScroll.repaint()
}
init {
add(realPanel, BorderLayout.CENTER)
inner class MapComponent : JComponent() {
init {
updateSize()
addMouseMotionListener(object : MouseMotionAdapter() {
override fun mouseDragged(e: MouseEvent) {
println("mouseDragged: $e")
onPressMouse(e.point)
}
})
addMouseListener(object : MouseAdapter() {
override fun mousePressed(e: MouseEvent) {
println("mousePressed: $e")
if (e.button == MouseEvent.BUTTON1) {
onPressMouse(e.point)
} else {
onRightPressMouse(e.point)
}
}
})
}
tileMapEditor.mapPanel.add(JScrollPane(object : JComponent() {
init {
this.preferredSize = Dimension(tmx.pixelWidth, tmx.pixelHeight)
}
var currentTileSelected = 1
override fun paintComponent(g: Graphics) {
val g = (g as Graphics2D)
fun onPressMouse(point: Point) {
val tileIndex = getTileIndex(point)
tmx.patternLayers[0].map[tileIndex.x, tileIndex.y] = RGBA(currentTileSelected)
repaint()
//println(tileIndex)
}
val TILE_WIDTH = tmx.tilewidth
val TILE_HEIGHT = tmx.tileheight
fun onRightPressMouse(point: Point) {
val tileIndex = getTileIndex(point)
currentTileSelected = tmx.patternLayers[0].map[tileIndex.x, tileIndex.y].value
}
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
val clipBounds = g.clipBounds
val displayTilesX = (clipBounds.width / TILE_WIDTH) + 2
val displayTilesY = (clipBounds.height / TILE_HEIGHT) + 2
val temp = Bitmap32(displayTilesX * TILE_WIDTH, displayTilesY * TILE_HEIGHT)
fun updateSize() {
this.preferredSize = Dimension((tmx.pixelWidth * scale).toInt(), (tmx.pixelHeight * scale).toInt())
}
val offsetX = clipBounds.x / TILE_WIDTH
val offsetY = clipBounds.y / TILE_HEIGHT
fun getTileIndex(coords: Point): Point = Point((coords.x / tmx.tilewidth / scale).toInt(), (coords.y / tmx.tileheight / scale).toInt())
for (layer in tmx.allLayers) {
when (layer) {
is TiledMap.Layer.Patterns -> {
for (x in 0 until displayTilesX) {
for (y in 0 until displayTilesY) {
val rx = x + offsetX
val ry = y + offsetY
override fun paintComponent(g: Graphics) {
val g = (g as Graphics2D)
if (rx < 0 || rx >= layer.map.width) continue
if (ry < 0 || ry >= layer.map.height) continue
val TILE_WIDTH = tmx.tilewidth
val TILE_HEIGHT = tmx.tileheight
val tileIdx = layer.map[rx, ry].value
val tile = tiles.getOrElse(tileIdx) { dummyTile }
val px0 = x * TILE_WIDTH
val py0 = y * TILE_HEIGHT
val px = rx * TILE_WIDTH
val py = ry * TILE_HEIGHT
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR)
val clipBounds = g.clipBounds
val displayTilesX = ((clipBounds.width / TILE_WIDTH / scale) + 3).toInt()
val displayTilesY = ((clipBounds.height / TILE_HEIGHT / scale) + 3).toInt()
val temp = Bitmap32((displayTilesX * TILE_WIDTH), (displayTilesY * TILE_HEIGHT))
temp._draw(tile.miniSlice, px0, py0, mix = true)
//temp.draw(tile.miniBmp32, px0, py0)
//g.drawImage(tile.miniImage, px, py, null)
/*
g.drawImage(
tile.image,
px, py, px + TILE_WIDTH, py + TILE_HEIGHT,
tile.area.left, tile.area.top, tile.area.right, tile.area.bottom,
null
)
*/
//g.color = Color.RED
//g.drawRect(px, py, TILE_WIDTH, TILE_HEIGHT)
val offsetX = (clipBounds.x / TILE_WIDTH / scale).toInt()
val offsetY = (clipBounds.y / TILE_HEIGHT / scale).toInt()
for (layer in tmx.allLayers) {
when (layer) {
is TiledMap.Layer.Patterns -> {
for (x in 0 until displayTilesX) {
for (y in 0 until displayTilesY) {
val rx = x + offsetX
val ry = y + offsetY
if (rx < 0 || rx >= layer.map.width) continue
if (ry < 0 || ry >= layer.map.height) continue
val tileIdx = layer.map[rx, ry].value
val tile = tiles.getOrNull(tileIdx)
if (tile != null) {
temp._draw(tile.miniSlice, x * TILE_WIDTH, y * TILE_HEIGHT, mix = true)
}
}
}
}
}
g.drawImage(temp.toAwt(), offsetX * TILE_WIDTH, offsetY * TILE_HEIGHT, null)
for (x in 0 until displayTilesX) {
for (y in 0 until displayTilesY) {
val rx = x + offsetX
val ry = y + offsetY
val px = rx * TILE_WIDTH
val py = ry * TILE_HEIGHT
g.color = Color.BLACK
g.drawRect(px, py, TILE_WIDTH, TILE_HEIGHT)
}
}
}
}), GridConstraints().also { it.fill = GridConstraints.FILL_BOTH })
//val oldTransform = g.transform
g.translate(offsetX * TILE_WIDTH * scale, offsetY * TILE_HEIGHT * scale)
g.scale(scale, scale)
g.drawImage(temp.toAwt(), 0, 0, null)
//g.transform = oldTransform
g.stroke = BasicStroke((1f / scale).toFloat())
g.color = Color.BLACK
//g.translate(offsetX * TILE_WIDTH * scale, offsetY * TILE_HEIGHT * scale)
for (y in 0 until displayTilesY) g.drawLine(0, y * TILE_HEIGHT, displayTilesX * TILE_WIDTH, y * TILE_HEIGHT)
for (x in 0 until displayTilesX) g.drawLine(x * TILE_WIDTH, 0, x * TILE_WIDTH, displayTilesY * TILE_HEIGHT)
}
}
val mapComponent = MapComponent()
val mapComponentScroll = JBScrollPane(mapComponent).also { scroll ->
//scroll.verticalScrollBar.unitIncrement = 16
}
fun updatedSize() {
tileMapEditor.leftSplitPane.dividerLocation = 200
tileMapEditor.rightSplitPane.dividerLocation = tileMapEditor.rightSplitPane.width - 200
}
init {
add(realPanel, BorderLayout.CENTER)
tileMapEditor.mapPanel.add(mapComponentScroll, GridConstraints().also { it.fill = GridConstraints.FILL_BOTH })
tileMapEditor.zoomInButton.addActionListener { scale *= 1.5 }
tileMapEditor.zoomOutButton.addActionListener { scale /= 1.5 }
updatedSize()
addComponentListener(object : ComponentAdapter() {
override fun componentResized(e: ComponentEvent) {
updatedSize()
}
})
}
}
@@ -135,3 +184,6 @@ class MyTileMapEditorFrame(val tmx: TiledMap) : JFrame() {
}
}
}
inline fun Bitmap32.anyFixed(callback: (RGBA) -> Boolean): Boolean = (0 until area).any { callback(data[it]) }
inline fun Bitmap32.allFixed(callback: (RGBA) -> Boolean): Boolean = (0 until area).all { callback(data[it]) }

View File

@@ -53,6 +53,18 @@
<text value="Settings"/>
</properties>
</component>
<component id="81436" class="javax.swing.JButton" binding="zoomInButton">
<constraints/>
<properties>
<text value="Zoom +"/>
</properties>
</component>
<component id="9b93c" class="javax.swing.JButton" binding="zoomOutButton">
<constraints/>
<properties>
<text value="Zoom -"/>
</properties>
</component>
</children>
</grid>
<vspacer id="ce228">
@@ -60,7 +72,7 @@
<grid row="1" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
</constraints>
</vspacer>
<splitpane id="19a4f">
<splitpane id="19a4f" binding="leftSplitPane">
<constraints>
<grid row="1" column="1" row-span="1" col-span="1" vsize-policy="3" hsize-policy="3" anchor="0" fill="3" indent="0" use-parent-layout="false">
<preferred-size width="200" height="200"/>
@@ -121,7 +133,7 @@
</tabbedpane>
</children>
</splitpane>
<splitpane id="e4503">
<splitpane id="e4503" binding="rightSplitPane">
<constraints>
<splitpane position="right"/>
</constraints>

View File

@@ -18,4 +18,8 @@ public class TileMapEditor {
public JPanel mapPanel;
public JTabbedPane tabbedPane4;
public JTabbedPane tabbedPane5;
public JButton zoomInButton;
public JButton zoomOutButton;
public JSplitPane rightSplitPane;
public JSplitPane leftSplitPane;
}