GFX.json
Wizard Boy & Girl + Orcs.
Animations: idle
, gesture
, walk
, attack
, death
Slime Blue
Download slime_blue
👁️
Slime Green
Download slime_green
👁️
Slime Yellow
Download slime_yellow
👁️
Usage code
val atlas = MutableAtlasUnit()
val wizardMale = KR.gfx.wizardM.__file.readImageDataContainer(ASE.toProps(), atlas)
val wizardFemale = KR.gfx.wizardF.__file.readImageDataContainer(ASE.toProps(), atlas)
imageDataView(wizardMale.default).scale(4, 4).also { it.smoothing = false }.also { view ->
println(view.animationNames)
view.animation = "idle"
view.play()
}
imageDataView(wizardFemale.default).scale(4, 4).also { it.smoothing = false }.also { view ->
println(view.animationNames)
view.animation = "gesture"
view.play()
view.x = 128f
}
Dungeon Tileset
Download dungeon_tileset_calciumtrice
👁️
LDtk Base Map
Download dungeon_tilesmap_calciumtrice
👁️
Required the LDtk extension.
CC0 VFX made with SpriteMancer by CodeManu:
Effect_Anima.gif
Download Effect_Anima.gif
👁️
Effect_BigHit.gif
Download Effect_BigHit.gif
👁️
Effect_BloodImpact.gif
Download Effect_BloodImpact.gif
👁️
Effect_Charged.gif
Download Effect_Charged.gif
👁️
Effect_Constellation.gif
Download Effect_Constellation.gif
👁️
Effect_DitheredFire.gif
Download Effect_DitheredFire.gif
👁️
Effect_EldenRing.gif
Download Effect_EldenRing.gif
👁️
Effect_ElectricShield.gif
Download Effect_ElectricShield.gif
👁️
Effect_Explosion.gif
Download Effect_Explosion.gif
👁️
Effect_Explosion2.gif
Download Effect_Explosion2.gif
👁️
Effect_Explosion2_4x.gif
Download Effect_Explosion2_4x.gif
👁️
Effect_Explosion_4x.gif
Download Effect_Explosion_4x.gif
👁️
Effect_FastPixelFire.gif
Download Effect_FastPixelFire.gif
👁️
Effect_Hyperspeed.gif
Download Effect_Hyperspeed.gif
👁️
Effect_Hyperspeed_4x.gif
Download Effect_Hyperspeed_4x.gif
👁️
Effect_Impact.gif
Download Effect_Impact.gif
👁️
Effect_Impact_2x.gif
Download Effect_Impact_2x.gif
👁️
Effect_Kabooms.gif
Download Effect_Kabooms.gif
👁️
Effect_Kabooms_2x.gif
Download Effect_Kabooms_2x.gif
👁️
Effect_Magma.gif
Download Effect_Magma.gif
👁️
Effect_PowerChords.gif
Download Effect_PowerChords.gif
👁️
Effect_PuffAndStars.gif
Download Effect_PuffAndStars.gif
👁️
Effect_SmallHit.gif
Download Effect_SmallHit.gif
👁️
Effect_Tentacles.gif
Download Effect_Tentacles.gif
👁️
Effect_TheVortex.gif
Download Effect_TheVortex.gif
👁️
Effect_Wheel.gif
Download Effect_Wheel.gif
👁️
Effect_Worm.gif
Download Effect_Worm.gif
👁️
Explosions
Download explosions
👁️
val animation = SpriteAnimation(
resourcesVfs["gfx/exp2.jpg"].readBitmapSlice().splitInRows(64, 64),
60.milliseconds
)
val random = Random(0L)
interval(0.02.seconds) {
sprite(animation).xy(random[0, 250], random[0, 250]).also { it.blendMode = BlendMode.SCREEN }
.also { sprite -> sprite.onAnimationCompleted { sprite.removeFromParent() } }
.playAnimation()
}
+ Sound
Download sound
val sound = resourcesVfs["sfx/explodify.mp3"].readSound()
val animation = SpriteAnimation(
resourcesVfs["gfx/exp2.jpg"].readBitmapSlice().splitInRows(64, 64),
60.milliseconds
)
val random = Random(0L)
interval(.25.seconds) {
sound.playNoCancel(1.playbackTimes).also { it.volume = .3 }
sprite(animation).xy(random[0, 250], random[0, 250]).also { it.blendMode = BlendMode.SCREEN }
.also { sprite -> sprite.onAnimationCompleted { sprite.removeFromParent() } }
.playAnimation()
}
LICENSE CC0: Created by https://kenney.nl/
Download icons.atlas
👁️
Untyped usage:
val icons = resourcesVfs["gfx/icons.atlas.json"].readAtlas()
image(icons["arrowDown.png"])
Typed usage:
You can also have type-safe access to the assets like this:
val icons = IconsAtlas(resourcesVfs["gfx/icons.atlas.json"].readAtlas())
image(icons.arrowDown)
Add this inline class for typed access:
inline class IconsAtlas(val atlas: Atlas) {
val arrowDown get() = atlas["arrowDown.png"]
val arrowLeft get() = atlas["arrowLeft.png"]
val arrowRight get() = atlas["arrowRight.png"]
val arrowUp get() = atlas["arrowUp.png"]
val audioOff get() = atlas["audioOff.png"]
val audioOn get() = atlas["audioOn.png"]
val barsHorizontal get() = atlas["barsHorizontal.png"]
val barsVertical get() = atlas["barsVertical.png"]
val button1 get() = atlas["button1.png"]
val button2 get() = atlas["button2.png"]
val button3 get() = atlas["button3.png"]
val buttonA get() = atlas["buttonA.png"]
val buttonB get() = atlas["buttonB.png"]
val buttonL get() = atlas["buttonL.png"]
val buttonL1 get() = atlas["buttonL1.png"]
val buttonL2 get() = atlas["buttonL2.png"]
val buttonR get() = atlas["buttonR.png"]
val buttonR1 get() = atlas["buttonR1.png"]
val buttonR2 get() = atlas["buttonR2.png"]
val buttonSelect get() = atlas["buttonSelect.png"]
val buttonStart get() = atlas["buttonStart.png"]
val buttonX get() = atlas["buttonX.png"]
val buttonY get() = atlas["buttonY.png"]
val checkmark get() = atlas["checkmark.png"]
val contrast get() = atlas["contrast.png"]
val cross get() = atlas["cross.png"]
val down get() = atlas["down.png"]
val downLeft get() = atlas["downLeft.png"]
val downRight get() = atlas["downRight.png"]
val exclamation get() = atlas["exclamation.png"]
val exit get() = atlas["exit.png"]
val exitLeft get() = atlas["exitLeft.png"]
val exitRight get() = atlas["exitRight.png"]
val export get() = atlas["export.png"]
val fastForward get() = atlas["fastForward.png"]
val gamepad get() = atlas["gamepad.png"]
val gamepad1 get() = atlas["gamepad1.png"]
val gamepad2 get() = atlas["gamepad2.png"]
val gamepad3 get() = atlas["gamepad3.png"]
val gamepad4 get() = atlas["gamepad4.png"]
val gear get() = atlas["gear.png"]
val home get() = atlas["home.png"]
val import get() = atlas["import.png"]
val information get() = atlas["information.png"]
val joystick get() = atlas["joystick.png"]
val joystickLeft get() = atlas["joystickLeft.png"]
val joystickRight get() = atlas["joystickRight.png"]
val joystickUp get() = atlas["joystickUp.png"]
val larger get() = atlas["larger.png"]
val leaderboardsComplex get() = atlas["leaderboardsComplex.png"]
val leaderboardsSimple get() = atlas["leaderboardsSimple.png"]
val left get() = atlas["left.png"]
val locked get() = atlas["locked.png"]
val massiveMultiplayer get() = atlas["massiveMultiplayer.png"]
val medal1 get() = atlas["medal1.png"]
val medal2 get() = atlas["medal2.png"]
val menuGrid get() = atlas["menuGrid.png"]
val menuList get() = atlas["menuList.png"]
val minus get() = atlas["minus.png"]
val mouse get() = atlas["mouse.png"]
val movie get() = atlas["movie.png"]
val multiplayer get() = atlas["multiplayer.png"]
val musicOff get() = atlas["musicOff.png"]
val musicOn get() = atlas["musicOn.png"]
val next get() = atlas["next.png"]
val open get() = atlas["open.png"]
val pause get() = atlas["pause.png"]
val phone get() = atlas["phone.png"]
val plus get() = atlas["plus.png"]
val power get() = atlas["power.png"]
val previous get() = atlas["previous.png"]
val question get() = atlas["question.png"]
val `return` get() = atlas["return.png"]
val rewind get() = atlas["rewind.png"]
val right get() = atlas["right.png"]
val save get() = atlas["save.png"]
val scrollHorizontal get() = atlas["scrollHorizontal.png"]
val scrollVertical get() = atlas["scrollVertical.png"]
val share1 get() = atlas["share1.png"]
val share2 get() = atlas["share2.png"]
val shoppingBasket get() = atlas["shoppingBasket.png"]
val shoppingCart get() = atlas["shoppingCart.png"]
val siganl1 get() = atlas["siganl1.png"]
val signal2 get() = atlas["signal2.png"]
val signal3 get() = atlas["signal3.png"]
val singleplayer get() = atlas["singleplayer.png"]
val smaller get() = atlas["smaller.png"]
val star get() = atlas["star.png"]
val stop get() = atlas["stop.png"]
val tablet get() = atlas["tablet.png"]
val target get() = atlas["target.png"]
val trashcan get() = atlas["trashcan.png"]
val trashcanOpen get() = atlas["trashcanOpen.png"]
val trophy get() = atlas["trophy.png"]
val unlocked get() = atlas["unlocked.png"]
val up get() = atlas["up.png"]
val upLeft get() = atlas["upLeft.png"]
val upRight get() = atlas["upRight.png"]
val video get() = atlas["video.png"]
val warning get() = atlas["warning.png"]
val wrench get() = atlas["wrench.png"]
val zoom get() = atlas["zoom.png"]
val zoomDefault get() = atlas["zoomDefault.png"]
val zoomIn get() = atlas["zoomIn.png"]
val zoomOut get() = atlas["zoomOut.png"]
}
SFX.json
https://opengameart.org/content/100-cc0-sfx
val sound = resourcesVfs["sfx/file.mp3"].readSound()
https://opengameart.org/content/8-bit-sound-effect-pack
val sound = resourcesVfs["sfx/file.mp3"].readSound()
You will have to install first MOD/S3M/XM support
Files from https://modarchive.org/index.php?request=view_by_license&query=cc0
defaultAudioFormats.register(MOD, S3M, XM)
val music = resourcesVfs["sfx/file.mod"].readMusic()
or
val music = resourcesVfs["sfx/file.mod"].readMusic(AudioDecodingProps.DEFAULT.copy(formats = MOD))
Fonts.json
Libraries & Modules.json
Adds initial experimental 3D.
import korlibs.image.bitmap.*
import korlibs.image.color.*
import korlibs.image.format.readNativeImage
import korlibs.io.async.launchImmediately
import korlibs.io.file.std.resourcesVfs
import korlibs.korge.KeepOnReload
import korlibs.korge.scene.Scene
import korlibs.korge.tween.tween
import korlibs.korge.view.*
import korlibs.korge3d.*
import korlibs.korge3d.shape.*
import korlibs.math.geom.*
import korlibs.time.seconds
class CratesScene : Scene() {
@KeepOnReload
var trans = Transform3D()
@KeepOnReload
var tick = 0.0
override suspend fun SContainer.sceneInit() {
//val korgeTex = KR.korge.read()
val crateTex = NativeImage(64, 64).context2d {
fill(Colors.ROSYBROWN) {
rect(0, 0, 64, 64)
}
stroke(Colors.SADDLEBROWN, 8f) {
rect(0, 0, 63, 63)
line(Point(0, 0), Point(63, 63))
}
}
//val crateTex = KR.crate.read().mipmaps(true)
//val crateTex = KR.dice.__file.readBitmap(QOI).mipmaps(true)
val crateMaterial = Material3D(diffuse = Material3D.LightTexture(crateTex))
solidRect(512, 512, MaterialColors.AMBER_200).alpha(0.5)
//image(korgeTex).alpha(0.5)
scene3D {
//camera.set(fov = 60.degrees, near = 0.3, far = 1000.0)
//light().position(0, 0, -3)
//polyline3d { }
polyline3D(Colors.BLUEVIOLET) {
moveTo(Vector3(-10f, 0f, 0f))
lineTo(Vector3(10f, 0f, 0f))
}
polyline3D(Colors.MEDIUMVIOLETRED) {
moveTo(Vector3(0f, -10f, 0f))
lineTo(Vector3(0f, 10f, 0f))
}
polyline3D(Colors["#8cb04d"]) {
moveTo(Vector3(0f, 0f, -10f))
lineTo(Vector3(0f, 0f, 10f))
}
polyline3D(Colors.WHITE) {
moveTo(Vector3(0f, 0f, 0f))
lineTo(Vector3(2f, 0f, 0f))
moveTo(Vector3(0f, 0f, 0f))
lineTo(Vector3(0f, 2f, 0f))
moveTo(Vector3(0f, 0f, 0f))
lineTo(Vector3(0f, 0f, 2f))
}
val cube1 = cube().material(crateMaterial)
sphere(1f).position(1, 0, 0).material(crateMaterial)
torus(1f).position(-1, 0, 0).material(crateMaterial)
cone(1f).position(0, -1, 0).material(crateMaterial)
cylinder(1f).position(0, -2, 0).material(crateMaterial)
//cube(2.0, 2.0)
val cube2 = cube().position(0, 2, 0).scale(1, 2, 1).rotation(0.degrees, 0.degrees, 45.degrees).material(crateMaterial)
val cube3 = cube().position(-5, 0, 0).material(crateMaterial)
val cube4 = cube().position(+5, 0, 0).material(crateMaterial)
val cube5 = cube().position(0, -5, 0).material(crateMaterial)
val cube6 = cube().position(0, +5, 0).material(crateMaterial)
val cube7 = cube().position(0, 0, -5).material(crateMaterial)
val cube8 = cube().position(0, 0, +5).material(crateMaterial)
addUpdater {
val angle = (tick / 4.0).degrees
trans.setTranslationAndLookAt(
cos(angle * 2) * 4, cos(angle * 3) * 4, -sin(angle) * 4, // Orbiting camera
0f, 1f, 0f
)
camera.transform.copyFrom(trans)
camera.invalidateRender()
tick += it.milliseconds / 16.0
}
launchImmediately {
while (true) {
tween(time = 16.seconds) {
cube1.modelMat.identity().rotate((it * 360).degrees, 0.degrees, 0.degrees)
cube2.modelMat.identity().rotate(0.degrees, (it * 360).degrees, 0.degrees)
}
}
}
}
solidRect(512, 512, Colors.BLUEVIOLET).position(views.virtualWidth, 0).anchor(1, 0).alpha(0.5)
//image(korgeTex).position(views.virtualWidth, 0).anchor(1, 0).alpha(0.5)
}
}
val config = SWFExportConfig(
rasterizerMethod = ShapeRasterizerMethod.NONE,
generateTextures = false,
graphicsRenderer = GraphicsRenderer.SYSTEM,
)
container {
this += resourcesVfs["morph.swf"].readSWF(views, config, false).createMainTimeLine()
this += resourcesVfs["dog.swf"].readSWF(views, config, false).createMainTimeLine()
this += resourcesVfs["test1.swf"].readSWF(views, config, false).createMainTimeLine().position(400, 0)
this += resourcesVfs["demo3.swf"].readSWF(views, config, false).createMainTimeLine()
}
Includes a Vampire and a Vamp spritesheet in .ASE format and code to load it.
Sprites from: https://finalbossblues.com/timefantasy/freebies/evil-transforming-vampires/
val atlas = MutableAtlasUnit(2048, 2048)
val characters = EvilTransformingVampires.readImages(atlas)
val player = imageDataView(characters.vampire, EvilTransformingVampires.Animations.DOWN, playing = true, smoothing = false)
.scale(4, 4)
.xy(120, 120)
fun update() {
val mx = if (input.keys[Key.LEFT]) -1 else if (input.keys[Key.RIGHT]) +1 else 0
val my = if (input.keys[Key.UP]) -1 else if (input.keys[Key.DOWN]) +1 else 0
if (mx == 0 && my == 0) player.stop() else player.play()
when {
mx < 0 -> player.animation = EvilTransformingVampires.Animations.LEFT
mx > 0 -> player.animation = EvilTransformingVampires.Animations.RIGHT
my < 0 -> player.animation = EvilTransformingVampires.Animations.UP
my > 0 -> player.animation = EvilTransformingVampires.Animations.DOWN
}
}
addUpdater { update() }
keys {
downFrame(Key.LEFT, 16.milliseconds) { player.x -= 4 }
downFrame(Key.RIGHT, 16.milliseconds) { player.x += 4 }
downFrame(Key.UP, 16.milliseconds) { player.y -= 4 }
downFrame(Key.DOWN, 16.milliseconds) { player.y += 4 }
}
Jitto
val jitto = JittoView().xy(256, 256).addTo(this)
while (true) {
jitto.interpolateTo(
Jitto(
rightHand = 10.degrees,
leftHand = 14.degrees,
leftLeg = +1f,
rightLeg = -1f,
leftEyeDist = 0f,
leftEyeAngle = 0.degrees,
rightEyeDist = 0f,
rightEyeAngle = 0.degrees,
rotation = 45.degrees
)
)
jitto.interpolateTo(
Jitto(
rightHand = -10.degrees,
leftHand = -14.degrees,
leftLeg = -1f,
rightLeg = +1f,
)
)
}
Load Dragonbones models for KorGE mascots Koral & Gest.
val db = KorgeDbFactory()
db.loadKorgeMascots()
val koral = db.buildArmatureDisplayKoral()
!!.position(100, 490)
.scale(SCALE)
.addTo(this)
.also { it.animation.play(KorgeMascotsAnimations.IDLE) }
Supports loading FLAC files that have a great compression rate.
https://en.wikipedia.org/wiki/FLAC
Explicitly loading an FLAC sound or music
val data = resourcesVfs["sounds/8Khz-Mono.flac"].readSound(AudioDecodingProps(formats = FLAC))
Getting information (length) of an FLAC file
println(FLAC.tryReadInfo(resourcesVfs["sounds/8Khz-Mono.flac"].open(), AudioDecodingProps(exactTimings = true)))
Registering the FLAC format, so it can detect the format automatically
defaultAudioFormats.register(FLAC)
val data = resourcesVfs["sounds/8Khz-Mono.flac"].readSound()
Support for MOD, XM & S3M music modules.
defaultAudioFormats.register(MOD, S3M, XM)
val music = resourcesVfs["GUITAROU.MOD"].readMusic()
var channel = music.play(times = infinitePlaybackTimes)
You can find some archives in this format here.
Supports loading OPUS files that have a great compression rate.
https://en.wikipedia.org/wiki/Opus_(audio_format)
Explicitly loading an OPUS sound or music
val data = resourcesVfs["sounds/8Khz-Mono.opus"].readSound(AudioDecodingProps(formats = OPUS))
Getting information (length) of an OPUS file
println(OPUS.tryReadInfo(resourcesVfs["sounds/8Khz-Mono.opus"].open(), AudioDecodingProps(exactTimings = true)))
Registering the OPUS format, so it can detect the format automatically
defaultAudioFormats.register(OPUS)
val data = resourcesVfs["sounds/8Khz-Mono.opus"].readSound()
Use KorGE Fleks, a KorGE port of the Fleks library, an ECS (Entity Component System library)
Supports triangulating, shape2D operations & mesh path finding.
Adds support for A*
pathfinding for bidimensional boolean arrays: true for blocking cells, false for available cells.
val map = AStar(BooleanArray2.fromString(mapOf('X' to true, '.' to false), false, code = """
.X...X....
.X...X....
.X.X.X....
...X.X....
...X......
...X......
""".trimIndent()))
val points = map.find(0, 1, 6, 2, findClosest = true, diagonals = true)
println(points)
// [(0, 1), (0, 2), (1, 3), (2, 2), (3, 1), (4, 2), (4, 3), (5, 4), (6, 3), (6, 2)]
Supports encoding and decoding JPG/JPEGs in pure Kotlin. Typically not necessary, since KorIM will use native decoders for JPEG and PNG. But useful to run on Node, or to be able to encode images.
val pngBytes = resourcesVfs["korge.png"].readBytes()
val bitmap = resourcesVfs["korge.png"].readBitmap()
val jpegBytes = measureTime({ JPEG.encode(bitmap, ImageEncodingProps(quality = 0.1)) }) { println("ENCODED in $it") }
val image = measureTime({ JPEG.decode(jpegBytes) }) { println("DECODED in $it") }
image(image).alpha(0.25)
Supports generating QR codes:
image(QR.msg("Hello from KorIM-QR!")).xy(128, 128).scale(6.0).also { it.smoothing = false }//.filters(DropshadowFilter(0.0, 0.0, blurRadius = 12.0, shadowColor = Colors.BLACK))
Constructing a QR builder
First we have to construct a QR
instance or use the companion object. This class can be constructed with a correctionLevel
parameter, and colors for the dark and light areas: colorDark
and colorLight
.
val qr = QR // Singleton
val qr = QR()
val qr = QR(colorDark = Colors.BLACK, colorLight = Colors.WHITE)
val qr = QR(correctLevel = QRErrorCorrectLevel.H)
Generating a Bitmap32 QR code
With a QR
instance already constructed, we can generate a QR code by using the provided methods in the class. QR codes support several kind of contents, and there are methods supporting those contents.
qr.msg(message)
qr.vCard(name, phone, email, url, addr, org, note)
qr.meCard(name, phone, email, url, addr, org)
qr.wifi(ssid, password, WifiKind.WEP)
qr.phone(phone)
qr.email(email)
qr.sms(number, message)
qr.geo(latitude, longitude)
qr.calendarEvent(summary, startDateTime, endDateTime, location, description)
Supports handling keyboard, real gamepad and virtual gamepad all in a simple interface.
val virtualController = virtualController(
sticks = listOf(
VirtualStickConfig(
left = Key.LEFT,
right = Key.RIGHT,
up = Key.UP,
down = Key.DOWN,
lx = GameButton.LX,
ly = GameButton.LY,
anchor = Anchor.BOTTOM_LEFT,
)
),
buttons = listOf(
VirtualButtonConfig(
key = Key.SPACE,
button = GameButton.BUTTON_SOUTH,
anchor = Anchor.BOTTOM_RIGHT,
),
VirtualButtonConfig(
key = Key.RETURN,
button = GameButton.BUTTON_NORTH,
anchor = Anchor.BOTTOM_RIGHT,
offset = Point(0f, -100f)
)
),
)
virtualController.apply {
down(GameButton.BUTTON_SOUTH) {
val isInGround = playerSpeed.y.isAlmostZero()
//if (isInGround) {
if (true) {
if (!jumping) {
jumping = true
updateState()
}
playerSpeed += Vector2(0, -5.5)
}
}
changed(GameButton.LX) {
if (it.new.absoluteValue < 0.01f) {
updated(right = it.new > 0f, up = true, scale = 1f)
}
}
addUpdater(60.hz) {
val lx = virtualController.lx
when {
lx < 0f -> {
updated(right = false, up = false, scale = lx.absoluteValue)
}
lx > 0f -> {
updated(right = true, up = false, scale = lx.absoluteValue)
}
}
}
}
Adds support for https://admob.google.com/.
Supports
KorGE 4.0.0
Usage
In build.gradle.kts
:
korge {
androidPermission("INTERNET")
androidManifestApplicationChunk(
"<meta-data android:name=\"com.google.android.gms.ads.APPLICATION_ID\" android:value=\"ca-app-pub-3395905965441916~3606887124\" />"
)
}
In your fun main() = Korge {
:
val admob = AdmobCreate(views, testing = true)
admob.bannerPrepare(
Admob.Config(
id = "ca-app-pub-3395905965441916/9312372956"
)
)
admob.bannerShow()
Adds Box2d support to KorGE:
solidRect(920, 100).xy(0, 620).registerBodyWithFixture(type = BodyType.STATIC, friction = 0.2, restitution = 0.2)
for (n in 0 until 5) {
//fastEllipse(Size(100, 100))
circle(50f)
//ellipse(Size(50, 50))
.xy(120 + 140 * n, 246)
.anchor(Anchor.CENTER)
.registerBodyWithFixture(
type = BodyType.DYNAMIC,
linearVelocityY = 6.0,
friction = 0.2,
restitution = 0.3 + (n * 0.1)
)
}
Use the LUA scripting engine in KorGE.
val globals = Globals().apply {
load(BaseLib())
load(PackageLib())
load(Bit32Lib())
load(TableLib())
load(StringLib())
load(CoroutineLib())
LoadState.install(this)
LuaC.install(this)
}
val textStack = uiVerticalStack(padding = 8f, adjustSize = false).xy(10, 10)
fun luaprintln(str: String) {
println("LUA_PRINTLN: $str")
textStack.text(str)
//kotlin.io.println()
}
// Overwrite print function
globals["print"] = object : VarArgFunction() {
override fun invoke(args: Varargs): Varargs {
val tostring = globals["tostring"]
val out = (1 .. args.narg())
.map { tostring.call(args.arg(it)).strvalue()!!.tojstring() }
luaprintln(out.joinToString("\t"))
return LuaValue.NONE
}
}
val result = globals.load(
//language=lua
"""
function max(a, b)
if (a > b) then
return a
else
return b
end
end
a = 10
res = 1 + 2 + a + max(20, 30)
print(res - 1)
b = {}
b[1] = 10
print(b)
for i=4,1,-1 do print(i) end
co = coroutine.create(function ()
for i=1,5 do
--print("co", i)
coroutine.yield("co" .. i, i + 1)
end
return "completed"
end)
function coroutine_it (co)
return function ()
local code, res = coroutine.resume(co)
if code then
return res
end
end
end
for i in coroutine_it(co) do
print("for", i)
end
--for i=1,12 do
-- local code, res = coroutine.resume(co)
-- print(code, res)
--end
print("ENDED!")
return res
""").callSuspend()
luaprintln(result.toString())
Examples:
val skeDeferred = async { Buffer(res["mecha_1002_101d_show/mecha_1002_101d_show_ske.dbbin"].readBytes()) }
val texDeferred = async { res["mecha_1002_101d_show/mecha_1002_101d_show_tex.json"].readString() }
val imgDeferred = async { res["mecha_1002_101d_show/mecha_1002_101d_show_tex.png"].readBitmap().mipmaps() }
val data = factory.parseDragonBonesData(skeDeferred.await())
val atlas = factory.parseTextureAtlasData(Json.parseFast(texDeferred.await())!!, imgDeferred.await())
val armatureDisplay = factory.buildArmatureDisplay("mecha_1002_101d")!!.position(0, 300).scale(SCALE)
println(armatureDisplay.animation.animationNames)
armatureDisplay.animation.play("idle")
this += armatureDisplay
Adds support for Spine.
val atlas = resourcesVfs["spineboy/spineboy-pma.atlas"].readAtlas(ImageDecodingProps(asumePremultiplied = true))
val skeletonData = resourcesVfs["spineboy/spineboy-pro.skel"].readSkeletonBinary(atlas, 0.6f)
fun createSkel(): Pair<Skeleton, AnimationState> {
val skeleton = Skeleton(skeletonData) // Skeleton holds skeleton state (bone positions, slot attachments, etc).
val stateData = AnimationStateData(skeletonData) // Defines mixing (crossfading) between animations.
stateData.setMix("run", "jump", 0.2f)
stateData.setMix("jump", "run", 0.2f)
val state = AnimationState(stateData) // Holds the animation state for a skeleton (current animation, time, etc).
state.timeScale = 0.5f // Slow all animations down to 50% speed.
// Queue animations on track 0.
state.setAnimation(0, "run", true)
state.addAnimation(0, "jump", false, 2f) // Jump after 2 seconds.
state.addAnimation(0, "run", true, 0f) // Run after the jump.
state.update(1f / 60f) // Update the animation time.
state.apply(skeleton) // Poses skeleton using current animations. This sets the bones' local SRT.
skeleton.updateWorldTransform() // Uses the bones' local SRT to compute their world SRT.
return skeleton to state
}
// Add view
container {
val (skeleton, state) = createSkel()
//speed = 2.0f
speed = 0.5f
scale(2.0)
position(200, 700)
skeletonView(skeleton, state).also { it.debugAnnotate = true }
solidRect(10.0, 10.0, Colors.RED).centered
filters(DropshadowFilter(shadowColor = Colors.RED))
}
container {
val (skeleton, state) = createSkel()
//speed = 2.0f
speed = 1.0f
scale(2.0)
position(900, 700)
skeletonView(skeleton, state).also { it.debugAnnotate = true }
solidRect(10.0, 10.0, Colors.RED).centered
//filters(DropshadowFilter(shadowColor = Colors.RED))
}
This module allows to automatically find IntArray2 patterns and update a StackedTileMap tile information from it. Similar to what LDtk does.
Some code to get started
suspend fun main() = Korge(windowSize = Size(256 * 2, 196 * 2)) {
val tilesIDC = resourcesVfs["gfx/tiles.ase"].readImageDataContainer(ASE)
val tiles = tilesIDC.mainBitmap.slice()
val tileSet = TileSet(tiles.splitInRows(16, 16).mapIndexed { index, slice -> TileSetTileInfo(index, slice) })
val tileMap = tileMap(TileMapData(32, 24, tileSet = tileSet))
val snakeMap = tileMap(TileMapData(32, 24, tileSet = tileSet))
val rules = CombinedRuleMatcher(WallsProvider, AppleProvider)
val ints = IntArray2(tileMap.map.width, tileMap.map.height, GROUND).observe { rect ->
IntGridToTileGrid(this.base as IntArray2, rules, tileMap.map, rect)
}
ints.lock {
ints[RectangleInt(0, 0, ints.width, 1)] = WALL
ints[RectangleInt(0, 0, 1, ints.height)] = WALL
ints[RectangleInt(0, ints.height - 1, ints.width, 1)] = WALL
ints[RectangleInt(ints.width - 1, 0, 1, ints.height)] = WALL
ints[RectangleInt(4, 4, ints.width / 2, 1)] = WALL
}
}
val GROUND = 0
val WALL = 1
val APPLE = 2
object AppleProvider : ISimpleTileProvider by (SimpleTileProvider(value = APPLE).also {
it.rule(SimpleRule(Tile(12)))
})
object WallsProvider : ISimpleTileProvider by (SimpleTileProvider(value = WALL).also {
it.rule(SimpleRule(Tile(16)))
it.rule(SimpleRule(Tile(17), right = true))
it.rule(SimpleRule(Tile(18), left = true, right = true))
it.rule(SimpleRule(Tile(19), left = true, down = true))
it.rule(SimpleRule(Tile(20), up = true, left = true, down = true))
it.rule(SimpleRule(Tile(21), up = true, left = true, right = true, down = true))
})
Some code to get started
fun chunk(pos: PointInt, data: String): StackedIntArray2 = StackedIntArray2(IntArray2(data, gen = { c, x, y -> if (c == '.') 0 else 1 }), startX = pos.x, startY = pos.y)
val fullMap = SparseChunkedStackedIntArray2()
fullMap.putChunk(chunk(PointInt(0, 0), """
.......
.......
.####..
.......
""".trimIndent()))
val result = fullMap.raycast(RayFromTwoPoints(Point(12, 12), Point(120, 80)), Size(8, 8)) { this.getLast(it.x, it.y) != 0 }
assertEquals(PointInt(18, 16), result?.toInt())
Visual testing scene
import korlibs.datastructure.*
import korlibs.image.atlas.*
import korlibs.image.bitmap.*
import korlibs.image.color.*
import korlibs.image.tiles.*
import korlibs.korge.*
import korlibs.korge.input.*
import korlibs.korge.scene.*
import korlibs.korge.view.*
import korlibs.korge.view.tiles.*
import korlibs.math.geom.*
import korlibs.math.geom.Circle
import korlibs.math.raycasting.*
suspend fun main() = Korge {
sceneContainer().changeTo({ RaycastingExampleScene()})
}
class RaycastingExampleScene : Scene() {
@KeepOnReload
var startPoint: Point = Point(300, 300)
@KeepOnReload
var endPoint: Point = Point(100, 100)
@KeepOnReload
var tileMap: IntArray2 = IntArray2(100, 100, 0).also {
for (n in 0 until 10) {
it[10 + n, 10] = 1
it[10, 10 + n] = 1
}
}
override suspend fun SContainer.sceneMain() {
val atlas = MutableAtlasUnit()
val bmp0 = atlas.add(Bitmap32(16, 16, Colors.TRANSPARENT.premultiplied)).slice
val bmp1 = atlas.add(Bitmap32(16, 16, Colors.BLUE.premultiplied)).slice
val tileSet = TileSet(TileSetTileInfo(0, bmp0), TileSetTileInfo(0, bmp1))
val tileMap = tileMap(tileMap, tileSet)
val cellSize = Size(bmp0.width, bmp0.height)
val overlay = graphics(renderer = GraphicsRenderer.SYSTEM) { }
text("""
mouse down: put blocks
shift+mouse down: remove blocks
ctrl+mouse down: to change the starting point
""".trimIndent())
fun updateOverlay(
startPoint: Point,
endPoint: Point,
result: Point?
) {
overlay.updateShape {
clear()
stroke(Colors.YELLOW) { line(startPoint, endPoint) }
stroke(Colors.GREEN) { circle(Circle(startPoint, 3f)) }
//stroke(Colors.WHITE) { circle(Circle(endPoint, 3f)) }
if (result != null) {
stroke(Colors.RED) {
circle(Circle(result, 3f))
}
}
}
}
fun downOnTileMapPos(mousePos: Point, add: Boolean, setStartPos: Boolean) {
if (setStartPos) {
startPoint = mousePos
return
}
val cell = (mousePos / cellSize).toInt()
tileMap.lock {
tileMap.stackedIntMap.setFirst(cell.x, cell.y, if (add) 1 else 0)
}
}
fun checkTileMap(mousePos: Point) {
val result = tileMap.stackedIntMap.raycast(RayFromTwoPoints(startPoint, mousePos), cellSize) {
//println("this.getLast(it.x, it.y): $it")
if (!this.inside(it.x, it.y)) return@raycast false
this.getLast(it.x, it.y) != 0
}
updateOverlay(startPoint, mousePos, result)
}
tileMap.mouse {
onDown {
downOnTileMapPos(it.currentPosLocal, !it.isShiftDown, it.isCtrlDown)
checkTileMap(it.currentPosLocal)
}
onMove {
if (it.pressing) downOnTileMapPos(it.currentPosLocal, !it.isShiftDown, it.isCtrlDown)
checkTileMap(it.currentPosLocal)
}
}
}
}
Use Compose Multiplatform along KorGE.
In order to use this module you need to do some manual additions for now:
In your root build.gradle.kts
plugins {
// ...
id("org.jetbrains.compose") version "1.4.0"
}
// ...
compose {
kotlinCompilerPluginArgs.add("suppressKotlinVersionCompatibilityCheck=1.8.21")
kotlinCompilerPlugin.set(dependencies.compiler.forKotlin("1.8.20"))
}
And it also requires at least kproject 0.2.7
.
Example. Using the Vampires asset:
import androidx.compose.runtime.*
import com.finalbossblues.timefantasy.*
import korlibs.event.*
import korlibs.image.bitmap.*
import korlibs.image.color.*
import korlibs.image.format.*
import korlibs.io.file.std.*
import korlibs.io.stream.*
import korlibs.korge.*
import korlibs.korge.compose.*
import korlibs.korge.scene.*
import korlibs.korge.view.*
import korlibs.math.geom.*
import korlibs.math.geom.Anchor
import korlibs.korge.compose.Image
import korlibs.korge.ui.*
import korlibs.korge.view.animation.*
suspend fun main() = Korge(
title = "Korge Compose",
windowSize = Size(512, 512),
backgroundColor = Colors["#2b2b2b"],
displayMode = KorgeDisplayMode(ScaleMode.SHOW_ALL, Anchor.TOP_LEFT, clipBorders = false),
forceRenderEveryFrame = false
) {
val sceneContainer = sceneContainer()
sceneContainer.changeTo({ MyScene() })
}
class MyScene : Scene() {
override suspend fun SContainer.sceneMain() {
setComposeContent(this) {
var width by remember { mutableStateOf(width.toInt()) }
var height by remember { mutableStateOf(height.toInt()) }
LaunchedEffect(true) {
fun onResized() {
val w = views.actualVirtualWidth
val h = views.actualVirtualHeight
width = w
height = h
this@sceneMain.size(w.toDouble(), h.toDouble())
}
onEvent(ReshapeEvent) {
onResized()
}
onResized()
//onStageResized { w, h ->
// //println("RESIZED: $w, $h")
// this@sceneMain.size(w.toDouble(), h.toDouble())
//}
}
MainApp(width, height)
}
}
}
@Composable
fun ImageDataView(
data: ImageData? = null,
animation: String? = null,
) {
ComposeKorgeView({
UIContainer(Size(32, 32)).apply {
imageDataView(data, animation)
.xy(16, 16)
.also { it.play() }
}
}, {
set(data) { (this.firstChild as korlibs.korge.view.animation.ImageDataView).data = data }
set(animation) { (this.firstChild as korlibs.korge.view.animation.ImageDataView).animation = animation }
})
}
@Composable
private fun MainApp(width: Int, height: Int) {
var color by remember { mutableStateOf(Colors.WHITE) }
var count by remember { mutableStateOf(0) }
var bitmap: BmpSlice by remember { mutableStateOf(Bitmaps.transparent) }
var vampire: ImageData? by remember { mutableStateOf(null) }
fun insert(digit: Int) {
count *= 10
count += digit
}
LaunchedEffect(true) {
val vampires = EvilTransformingVampires.readImages()
vampire = vampires?.vampire
bitmap = vampires?.vampire?.defaultAnimation?.firstFrame?.bitmap?.slice() ?: Bitmaps.white
println("1")
//bitmap = resourcesVfs["korge-30.jpg"].readBitmap().slice()
}
VStack(width.toFloat(), adjustSize = true) {
Image(bitmap)
ImageDataView(vampire, "down")
Text("$count", color)
HStack {
Button("-") { count-- }
Button("+") { count++ }
}
}
}