Examples

Complete working code samples

Examples

Minimal App

The simplest possible application — opens a window that closes on ESC.

import org.lwjgl.glfw.GLFW.*
import ua.terra.renderengine.RenderEngineCore

class MinimalApp : RenderEngineCore(
    "Minimal App",
    -1, -1,     // centered
    800, 600,   // size
    60          // tick rate
) {
    override fun onEnable() {
        keyboard.onKeyPress(GLFW_KEY_ESCAPE) {
            window.close()
        }
    }

    override fun tick() {
        // Game logic here
    }

    override fun onRender() {
        renderEngine.flush()
    }
}

fun main() {
    check(glfwInit()) { "Failed to init GLFW" }
    MinimalApp().enable()
}

Key Points

  • Extend RenderEngineCore
  • Implement onEnable(), tick(), onRender()
  • Call glfwInit() before creating engine
  • Call enable() to start

Player with Camera

Controllable player with a camera that follows.

import org.lwjgl.glfw.GLFW.*
import ua.terra.renderengine.RenderEngineCore
import ua.terra.renderengine.camera.CameraTarget
import ua.terra.renderengine.camera.CameraBounds
import ua.terra.renderengine.util.Point

class GameWithPlayer : RenderEngineCore(
    "Player Demo", -1, -1, 1280, 720, 60
) {
    private lateinit var player: Player

    override fun onEnable() {
        player = Player(640f, 360f)

        // Camera follows player
        camera.initialize(player)
        camera.bounds = CameraBounds(0f, 0f, 1280f, 720f)
        camera.minZoom = 0.5f
        camera.maxZoom = 3f

        // Movement
        keyboard.onKeyPressed(GLFW_KEY_W) { player.vy = -3f }
        keyboard.onKeyPressed(GLFW_KEY_S) { player.vy = 3f }
        keyboard.onKeyPressed(GLFW_KEY_A) { player.vx = -3f }
        keyboard.onKeyPressed(GLFW_KEY_D) { player.vx = 3f }

        // Zoom
        keyboard.onKeyPress(GLFW_KEY_EQUAL) { camera.zoomIn() }
        keyboard.onKeyPress(GLFW_KEY_MINUS) { camera.zoomOut() }

        keyboard.onKeyPress(GLFW_KEY_ESCAPE) { window.close() }
    }

    override fun tick() {
        camera.tick()
        player.update()
    }

    override fun onRender() {
        // Draw player as rectangle
        renderEngine.geometryRenderer.renderRectangle(
            player.x - 16f, player.y - 16f,
            32f, 32f,
            color = 0xFFFFFFFF.toInt()
        )
        renderEngine.flush()
    }
}

class Player(var x: Float, var y: Float) : CameraTarget {
    var vx = 0f
    var vy = 0f

    override fun getCameraPoint() = Point(x, y)

    fun update() {
        x = (x + vx).coerceIn(0f, 1280f)
        y = (y + vy).coerceIn(0f, 720f)
        vx = 0f
        vy = 0f
    }
}

fun main() {
    check(glfwInit()) { "Failed to init GLFW" }
    GameWithPlayer().enable()
}

Key Points

  • Implement CameraTarget for followable objects
  • Call camera.tick() every tick
  • Use camera.screenToScene() to convert coordinates

Text & UI

Rendering text with different fonts and sizes.

import org.lwjgl.glfw.GLFW.*
import ua.terra.renderengine.RenderEngineCore
import ua.terra.renderengine.resource.ResourcePackManager

class UIDemo : RenderEngineCore(
    "UI Demo", -1, -1, 1024, 768, 60
) {
    private var showDebug = true

    override fun onEnable() {
        // Register fonts
        fontRegistry.register("ui",
            ResourcePackManager.getResourcePath("fonts/Inter.ttf"), 18)
        fontRegistry.register("title",
            ResourcePackManager.getResourcePath("fonts/Inter.ttf"), 48)
        fontRegistry.initialize()

        keyboard.onKeyPress(GLFW_KEY_F3) { showDebug = !showDebug }
        keyboard.onKeyPress(GLFW_KEY_ESCAPE) { window.close() }
    }

    override fun tick() {}

    override fun onRender() {
        val title = fontRegistry.get("title")
        val ui = fontRegistry.get("ui")

        // Title
        renderEngine.textRenderer.renderText(
            "My Game",
            x = 50f, y = 50f,
            font = title,
            color = 0xFFFFFFFF.toInt()
        )

        // FPS
        renderEngine.textRenderer.renderText(
            "FPS: ${metrics.fps.toInt()}",
            x = 50f, y = 120f,
            font = ui
        )

        // Debug info
        if (showDebug) {
            renderEngine.textRenderer.renderText(
                "Press F3 to toggle debug",
                x = 50f, y = 150f,
                font = ui,
                color = 0xFF888888.toInt()
            )
        }

        renderEngine.flush()
    }
}

fun main() {
    check(glfwInit()) { "Failed to init GLFW" }
    UIDemo().enable()
}

Input Handling

Complete keyboard and mouse input with cooldowns.

import org.lwjgl.glfw.GLFW.*
import ua.terra.renderengine.RenderEngineCore
import ua.terra.renderengine.util.Cooldown

class InputDemo : RenderEngineCore(
    "Input Demo", -1, -1, 1280, 720, 60
) {
    private var mouseX = 0f
    private var mouseY = 0f
    private var clickX = 0f
    private var clickY = 0f
    private val clickCooldown = Cooldown(300)

    override fun onEnable() {
        // Keyboard
        keyboard.onKeyPress(GLFW_KEY_ESCAPE) { window.close() }
        keyboard.onKeyPress(GLFW_KEY_SPACE) { println("Jump!") }
        keyboard.onKeyPressed(GLFW_KEY_W) { println("Moving...") }
        keyboard.onKeyRelease(GLFW_KEY_SPACE) { println("Jump ended") }

        // Mouse
        mouse.onMouseMove { x, y, _, _ ->
            mouseX = x.toFloat()
            mouseY = y.toFloat()
        }

        mouse.onMouseClick(GLFW_MOUSE_BUTTON_LEFT) { x, y ->
            if (clickCooldown.isEnded()) {
                clickX = x.toFloat()
                clickY = y.toFloat()
                clickCooldown.start()
            }
        }

        mouse.onMouseScroll { offset ->
            println("Scroll: $offset")
        }
    }

    override fun tick() {}

    override fun onRender() {
        // Cursor indicator
        renderEngine.geometryRenderer.renderRectangle(
            mouseX - 4f, mouseY - 4f, 8f, 8f,
            color = 0xFF00FF00.toInt()
        )

        // Click marker
        if (clickX != 0f) {
            renderEngine.geometryRenderer.renderRectangle(
                clickX - 10f, clickY - 10f, 20f, 20f,
                color = 0xFFFF0000.toInt()
            )
        }

        renderEngine.flush()
    }
}

fun main() {
    check(glfwInit()) { "Failed to init GLFW" }
    InputDemo().enable()
}

Input Types

  • onKeyPress — once on key down
  • onKeyPressed — every frame while held
  • onKeyRelease — once on key up
  • Cooldown — prevents action spam

Animation

Basic animation with physics.

import org.lwjgl.glfw.GLFW.*
import ua.terra.renderengine.RenderEngineCore
import kotlin.math.sin

class AnimationDemo : RenderEngineCore(
    "Animation", -1, -1, 1280, 720, 60
) {
    private var rotation = 0f
    private var ballY = 200f
    private var ballVelocity = 0f
    private val gravity = 0.5f

    override fun onEnable() {
        keyboard.onKeyPress(GLFW_KEY_ESCAPE) { window.close() }
    }

    override fun tick() {
        // Rotation
        rotation += 2f
        if (rotation >= 360f) rotation -= 360f

        // Bouncing ball
        ballVelocity += gravity
        ballY += ballVelocity
        if (ballY >= 600f) {
            ballY = 600f
            ballVelocity *= -0.8f
        }
    }

    override fun onRender() {
        // Rotating square
        renderEngine.geometryRenderer.renderRectangle(
            200f - 30f, 200f - 30f, 60f, 60f,
            rotation = rotation.toDouble(),
            color = 0xFF3B82F6.toInt()
        )

        // Bouncing ball
        renderEngine.geometryRenderer.renderCircle(
            500f, ballY, 30f,
            color = 0xFFEF4444.toInt()
        )

        // Sine wave
        val t = (System.currentTimeMillis() % 2000) / 2000f
        val waveX = 700f + t * 400f
        val waveY = 400f + sin(t * Math.PI * 4).toFloat() * 100f
        renderEngine.geometryRenderer.renderCircle(
            waveX, waveY, 15f,
            color = 0xFF22C55E.toInt()
        )

        renderEngine.flush()
    }
}

fun main() {
    check(glfwInit()) { "Failed to init GLFW" }
    AnimationDemo().enable()
}

Animation Tips

  • Update state in tick()
  • Render current state in onRender()
  • Engine interpolates automatically between ticks