diff --git a/build.gradle b/build.gradle index 450d5cb..b26c49d 100644 --- a/build.gradle +++ b/build.gradle @@ -42,9 +42,15 @@ kotlin { linuxTest { } } +} +task copyResources(type: Copy) { + from('res') + into('build/bin/linux/main/release/executable/res') } +compileKotlinLinux.finalizedBy(copyResources) + task runProgram { def buildType = 'release' // Change to 'debug' to run application with debug symbols. dependsOn "link${buildType.capitalize()}ExecutableLinux" diff --git a/res/images/spaceShips_001.png b/res/images/spaceShips_001.png new file mode 100644 index 0000000..cc6f240 Binary files /dev/null and b/res/images/spaceShips_001.png differ diff --git a/src/linuxMain/kotlin/sample/GameState.kt b/src/linuxMain/kotlin/sample/GameState.kt new file mode 100644 index 0000000..4f21667 --- /dev/null +++ b/src/linuxMain/kotlin/sample/GameState.kt @@ -0,0 +1,6 @@ +package sample + +sealed class GameState { + object Running : GameState() + object Done : GameState() +} \ No newline at end of file diff --git a/src/linuxMain/kotlin/sample/SDLRenderer.kt b/src/linuxMain/kotlin/sample/SDLRenderer.kt new file mode 100644 index 0000000..905ee5a --- /dev/null +++ b/src/linuxMain/kotlin/sample/SDLRenderer.kt @@ -0,0 +1,41 @@ +package sample + +import cnames.structs.SDL_Window +import cnames.structs.SDL_Texture +import kotlinx.cinterop.CPointer +import kotlinx.cinterop.toKString +import sdl2.* + +@ExperimentalUnsignedTypes +class SDLRenderer(window: CPointer) { + + private val renderer: CPointer = + SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED or SDL_RENDERER_PRESENTVSYNC) ?: throw NullPointerException("SDL Renderer returned null") + + init { + SDL_SetRenderDrawColor(renderer, 0xFF, 0xFF, 0xFF, 0xFF) + } + + fun render(vararg textures: CPointer) { + SDL_RenderClear(renderer) + for (texture in textures) + SDL_RenderCopy(renderer, texture, null, null) + + SDL_RenderPresent(renderer) + } + + + @ExperimentalUnsignedTypes + fun loadImage(path: String): CPointer? { + + val img = IMG_Load(path)!! + val finalSurface = SDL_CreateTextureFromSurface(renderer, img) ?: throw Exception("SDL Error when trying to load texture: ${SDL_GetError()?.toKString()}") + SDL_FreeSurface(img) + return finalSurface + } + + fun destroy() { + SDL_DestroyRenderer(renderer) + } + +} \ No newline at end of file diff --git a/src/linuxMain/kotlin/sample/Sample.kt b/src/linuxMain/kotlin/sample/Sample.kt index 8ba00fd..73eb4b2 100644 --- a/src/linuxMain/kotlin/sample/Sample.kt +++ b/src/linuxMain/kotlin/sample/Sample.kt @@ -1,112 +1,6 @@ package sample -import kotlinx.cinterop.* import kotlinx.coroutines.runBlocking -import sdl2.* -import kotlin.math.roundToInt -import kotlin.system.getTimeMillis @ExperimentalUnsignedTypes -fun main(): Unit = runBlocking { - memScoped { - val window = SDL_CreateWindow( - title = "Hello Kotlin/Native", - x = SDL_WINDOWPOS_CENTERED.toInt(), - y = SDL_WINDOWPOS_CENTERED.toInt(), - w = 640, - h = 480, - flags = SDL_WINDOW_SHOWN - )!! - - val gameContext = GameContext(window) - val ship: SDL_Surface = alloc() - - paintScreen(gameContext) - - val sdlEvent: SDL_Event = alloc() - - gameContext.loop { - sdlEvent.resolveInputs { event -> - when(event) { - SDL_QUIT -> gameContext.quit() - } - } - - - - } - - - SDL_DestroyWindow(window) - SDL_Quit() - - } -} - -@ExperimentalUnsignedTypes -data class GameContext( - val window: CPointer, - val primarySurface: CPointer = SDL_GetWindowSurface(window)!! -) { - private var state: GameState = GameState.Running - - fun loop(function: () -> Unit) { - val tickrate = 1000 / 60 - - while (state is GameState.Running) { - val startTime = getTimeMillis() - function() - val endTime = getTimeMillis() - val timeDiff = endTime - startTime - - if (timeDiff < tickrate) - SDL_Delay((tickrate - timeDiff).toUInt()) - } - } - - fun quit() { - state = GameState.Done - } - - fun currentState() = state -} - -sealed class GameState { - object Running : GameState() - object Done : GameState() -} - -@ExperimentalUnsignedTypes -fun SDL_Event.resolveInputs(func: (Uint32) -> Unit) { - SDL_PollEvent(this.ptr) - - func(this.type) - -} - - -fun CPointer.fill(rect: CValue, r: Uint8, g: Uint8, b: Uint8) = - SDL_FillRect(this, rect, SDL_MapRGB(this.pointed.format, r, g, b)) - -@ExperimentalUnsignedTypes -fun paintScreen(context: GameContext) = memScoped { - val step: Float = 255f / 639 - - val rect = alloc() - - for (line in 0..639) { - val color = (line * step).roundToInt() - - rect.apply { - x = line - y = 0 - w = 1 - h = 480 - } - - context.primarySurface.fill(rect.readValue(), color.toUByte(), color.toUByte(), color.toUByte()) - } - - - SDL_UpdateWindowSurface(context.window) -} +fun main(): Unit = runBlocking { ShootumsGame().run() } \ No newline at end of file diff --git a/src/linuxMain/kotlin/sample/ShootumsGame.kt b/src/linuxMain/kotlin/sample/ShootumsGame.kt new file mode 100644 index 0000000..59b76aa --- /dev/null +++ b/src/linuxMain/kotlin/sample/ShootumsGame.kt @@ -0,0 +1,69 @@ +package sample + +import cnames.structs.SDL_Window +import kotlinx.cinterop.* +import platform.posix.EXDEV +import sdl2.* +import kotlin.math.roundToInt +import kotlin.system.getTimeMillis + +@ExperimentalUnsignedTypes +class ShootumsGame { + private val arena = Arena() + val window: CPointer = SDL_CreateWindow( + title = "SHIPPPPPPPPPPPPPPPPPPPPPPPP", + x = SDL_WINDOWPOS_CENTERED.toInt(), + y = SDL_WINDOWPOS_CENTERED.toInt(), + w = 640, + h = 480, + flags = SDL_WINDOW_SHOWN + )!! + private val renderer = SDLRenderer(window) + private var state: GameState = GameState.Running + private val sdlEvent: SDL_Event = arena.alloc() + + + fun run() = memScoped { + val tickrate = 1000 / 60 + val ship = renderer.loadImage("res/images/spaceShips_001.png") ?: throw Exception("Could not load ship: ${SDL_GetError()?.toKString()}") + + while (state is GameState.Running) { + val startTime = getTimeMillis() + + sdlEvent.resolveInputs { event -> + when (event) { + SDL_QUIT -> quit() + } + } + + renderer.render(ship) + + + val endTime = getTimeMillis() + val timeDiff = endTime - startTime + + if (timeDiff < tickrate) + SDL_Delay((tickrate - timeDiff).toUInt()) + } + + renderer.destroy() + SDL_DestroyWindow(window) + SDL_Quit() + } + + fun quit() { + state = GameState.Done + } + + fun currentState() = state + + @ExperimentalUnsignedTypes + private fun SDL_Event.resolveInputs(func: (Uint32) -> Unit) { + SDL_PollEvent(this.ptr) + + func(this.type) + + } + + +} \ No newline at end of file diff --git a/src/nativeInterop/cinterop/sdl2.def b/src/nativeInterop/cinterop/sdl2.def index 21a014a..36deb4f 100644 --- a/src/nativeInterop/cinterop/sdl2.def +++ b/src/nativeInterop/cinterop/sdl2.def @@ -1,4 +1,4 @@ -headers = SDL.h stdlib.h time.h +headers = SDL.h stdlib.h time.h SDL_image.h entryPoint = SDL_main headerFilter = SDL* stdlib.h time.h @@ -9,4 +9,4 @@ compilerOpts.linux = -D_REENTRANT compilerOpts.ios = linkerOpts.osx = -F ${System.getProperty("user.home")}/Library/Frameworks -F /Library/Frameworks -framework SDL2 -linkerOpts.linux = -L/usr/lib64 -L/usr/lib/x86_64-linux-gnu -L/lib/x86_64-linux-gnu/ -lSDL2 +linkerOpts.linux = -L/usr/lib64 -L/usr/lib/x86_64-linux-gnu -L/lib/x86_64-linux-gnu/ -lSDL2 -lSDL2_image