mirror of
https://github.com/jlengrand/korge-intellij-plugin.git
synced 2026-03-10 00:21:21 +00:00
Some tilemap editor improvements
This commit is contained in:
10
README.md
10
README.md
@@ -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
|
||||
```
|
||||
|
||||
@@ -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]) }
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user