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` Output: `build/distributions/KorgePlugin.zip`
### Testing ### Executing from Source
``` ```
./gradlew runIde ./gradlew runIde
@@ -26,4 +26,10 @@ Output: `build/distributions/KorgePlugin.zip`
This should launch a new intelliJ Community Edition with the KorGE plugin. 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 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 package com.soywiz.korge.intellij.editor.tile
import com.intellij.ui.components.*
import com.intellij.uiDesigner.core.* import com.intellij.uiDesigner.core.*
import com.soywiz.korge.tiled.* import com.soywiz.korge.tiled.*
import com.soywiz.korim.awt.* import com.soywiz.korim.awt.*
import com.soywiz.korim.bitmap.* import com.soywiz.korim.bitmap.*
import com.soywiz.korim.color.*
import com.soywiz.korio.file.std.* import com.soywiz.korio.file.std.*
import com.soywiz.korma.geom.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
import java.awt.* import java.awt.*
import java.awt.image.* import java.awt.event.*
import javax.swing.* import javax.swing.*
class MyTileMapEditorPanel(val tmx: TiledMap) : JPanel(BorderLayout()) { 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 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) { data class TileInfo(val bmp32: Bitmap32) {
val miniImage = awt.getSubimage(area.x, area.y, area.width, area.height) val miniSlice = bmp32.slice()
val miniBmp32 = image.copySliceWithSize(area.x, area.y, area.width, area.height)
val miniSlice = miniBmp32.sliceWithSize(0, 0, area.width, area.height)
} }
private val emptyImage = Bitmaps.transparent.bmp val tiles = Array<TileInfo?>(maxTileGid) { null }.also { tiles ->
private val emptyImageAwt = emptyImage.toAwt()
val dummyTile = TileInfo(emptyImage, emptyImageAwt, Bitmaps.transparent.bounds)
val tiles: Array<TileInfo> = Array(maxTileGid) { dummyTile }.also { tiles ->
for (tileset in tmx.tilesets) { for (tileset in tmx.tilesets) {
val tex = tileset.tileset.base.toBMP32()
val tex2 = tex.toAwt()
for (tileIdx in tileset.tileset.textures.indices) { for (tileIdx in tileset.tileset.textures.indices) {
val tile = tileset.tileset.textures[tileIdx] val tile = tileset.tileset.textures[tileIdx]
if (tile != null) { 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 val realPanel = tileMapEditor.contentPanel
var scale = 2.0
set(value) {
field = value
mapComponent.updateSize()
mapComponent.revalidate()
mapComponentScroll.revalidate()
mapComponentScroll.repaint()
}
init { inner class MapComponent : JComponent() {
add(realPanel, BorderLayout.CENTER) 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() { var currentTileSelected = 1
init {
this.preferredSize = Dimension(tmx.pixelWidth, tmx.pixelHeight)
}
override fun paintComponent(g: Graphics) { fun onPressMouse(point: Point) {
val g = (g as Graphics2D) val tileIndex = getTileIndex(point)
tmx.patternLayers[0].map[tileIndex.x, tileIndex.y] = RGBA(currentTileSelected)
repaint()
//println(tileIndex)
}
val TILE_WIDTH = tmx.tilewidth fun onRightPressMouse(point: Point) {
val TILE_HEIGHT = tmx.tileheight val tileIndex = getTileIndex(point)
currentTileSelected = tmx.patternLayers[0].map[tileIndex.x, tileIndex.y].value
}
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); fun updateSize() {
val clipBounds = g.clipBounds this.preferredSize = Dimension((tmx.pixelWidth * scale).toInt(), (tmx.pixelHeight * scale).toInt())
val displayTilesX = (clipBounds.width / TILE_WIDTH) + 2 }
val displayTilesY = (clipBounds.height / TILE_HEIGHT) + 2
val temp = Bitmap32(displayTilesX * TILE_WIDTH, displayTilesY * TILE_HEIGHT)
val offsetX = clipBounds.x / TILE_WIDTH fun getTileIndex(coords: Point): Point = Point((coords.x / tmx.tilewidth / scale).toInt(), (coords.y / tmx.tileheight / scale).toInt())
val offsetY = clipBounds.y / TILE_HEIGHT
for (layer in tmx.allLayers) { override fun paintComponent(g: Graphics) {
when (layer) { val g = (g as Graphics2D)
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 val TILE_WIDTH = tmx.tilewidth
if (ry < 0 || ry >= layer.map.height) continue val TILE_HEIGHT = tmx.tileheight
val tileIdx = layer.map[rx, ry].value g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR)
val tile = tiles.getOrElse(tileIdx) { dummyTile } val clipBounds = g.clipBounds
val px0 = x * TILE_WIDTH val displayTilesX = ((clipBounds.width / TILE_WIDTH / scale) + 3).toInt()
val py0 = y * TILE_HEIGHT val displayTilesY = ((clipBounds.height / TILE_HEIGHT / scale) + 3).toInt()
val px = rx * TILE_WIDTH val temp = Bitmap32((displayTilesX * TILE_WIDTH), (displayTilesY * TILE_HEIGHT))
val py = ry * TILE_HEIGHT
temp._draw(tile.miniSlice, px0, py0, mix = true) val offsetX = (clipBounds.x / TILE_WIDTH / scale).toInt()
//temp.draw(tile.miniBmp32, px0, py0) val offsetY = (clipBounds.y / TILE_HEIGHT / scale).toInt()
//g.drawImage(tile.miniImage, px, py, null)
/* for (layer in tmx.allLayers) {
g.drawImage( when (layer) {
tile.image, is TiledMap.Layer.Patterns -> {
px, py, px + TILE_WIDTH, py + TILE_HEIGHT, for (x in 0 until displayTilesX) {
tile.area.left, tile.area.top, tile.area.right, tile.area.bottom, for (y in 0 until displayTilesY) {
null val rx = x + offsetX
) val ry = y + offsetY
*/
//g.color = Color.RED if (rx < 0 || rx >= layer.map.width) continue
//g.drawRect(px, py, TILE_WIDTH, TILE_HEIGHT) 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"/> <text value="Settings"/>
</properties> </properties>
</component> </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> </children>
</grid> </grid>
<vspacer id="ce228"> <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"/> <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> </constraints>
</vspacer> </vspacer>
<splitpane id="19a4f"> <splitpane id="19a4f" binding="leftSplitPane">
<constraints> <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"> <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"/> <preferred-size width="200" height="200"/>
@@ -121,7 +133,7 @@
</tabbedpane> </tabbedpane>
</children> </children>
</splitpane> </splitpane>
<splitpane id="e4503"> <splitpane id="e4503" binding="rightSplitPane">
<constraints> <constraints>
<splitpane position="right"/> <splitpane position="right"/>
</constraints> </constraints>

View File

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