diff --git a/Makefile b/Makefile index b149c0a..7bae0be 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,19 @@ -.PHONY: shaders-vulkan shaders-d3d12 shaders-metal shaders-all bake toad check test help +.PHONY: shaders-vulkan shaders-d3d12 shaders-metal shaders-all bake toad hello_sprite crowd camera_sandbox clips check test help help: @echo "Targets:" - @echo " bake Bake all characters under content/characters/" - @echo " shaders-vulkan Compile SPIR-V into shaders/vulkan/" - @echo " shaders-d3d12 Compile DXIL into shaders/d3d12/ (needs shadercross)" - @echo " shaders-metal Compile MSL into shaders/metal/ (needs shadercross)" - @echo " shaders-all Build all shader backends" - @echo " test Run engine unit tests" - @echo " check Typecheck examples/toad" - @echo " toad Run the toad example" + @echo " bake Bake all characters under content/characters/" + @echo " shaders-vulkan Compile SPIR-V into shaders/vulkan/" + @echo " shaders-d3d12 Compile DXIL into shaders/d3d12/ (needs shadercross)" + @echo " shaders-metal Compile MSL into shaders/metal/ (needs shadercross)" + @echo " shaders-all Build all shader backends" + @echo " test Run engine unit tests" + @echo " check Typecheck all examples" + @echo " toad Run the toad example (full demo)" + @echo " hello_sprite Minimal load + draw" + @echo " crowd Many sprites, one Character_Data" + @echo " camera_sandbox Pan camera / Space toggles follow" + @echo " clips Keys 1/2 switch idle/walk" bake: ./scripts/bake_all.sh @@ -30,6 +34,22 @@ test: check: odin check examples/toad -collection:pkg=. + odin check examples/hello_sprite -collection:pkg=. + odin check examples/crowd -collection:pkg=. + odin check examples/camera_sandbox -collection:pkg=. + odin check examples/clips -collection:pkg=. toad: odin run examples/toad -collection:pkg=. + +hello_sprite: + odin run examples/hello_sprite -collection:pkg=. + +crowd: + odin run examples/crowd -collection:pkg=. + +camera_sandbox: + odin run examples/camera_sandbox -collection:pkg=. + +clips: + odin run examples/clips -collection:pkg=. diff --git a/engine/input.odin b/engine/input.odin index 68283e9..b50ad33 100644 --- a/engine/input.odin +++ b/engine/input.odin @@ -7,6 +7,11 @@ Key :: enum { D, Left, Right, + Up, + Down, + Space, + N1, + N2, } key_down :: proc(key: Key) -> bool { @@ -23,6 +28,16 @@ key_down :: proc(key: Key) -> bool { scan_code = .LEFT case .Right: scan_code = .RIGHT + case .Up: + scan_code = .UP + case .Down: + scan_code = .DOWN + case .Space: + scan_code = .SPACE + case .N1: + scan_code = ._1 + case .N2: + scan_code = ._2 } return keys[scan_code] } diff --git a/examples/camera_sandbox/main.odin b/examples/camera_sandbox/main.odin new file mode 100644 index 0000000..6e22feb --- /dev/null +++ b/examples/camera_sandbox/main.odin @@ -0,0 +1,58 @@ +package main + +import eng "pkg:engine" + +// Camera sandbox: toad stays put; arrows pan the camera. Space toggles follow mode. +main :: proc() { + app: eng.App + if !eng.init(&app, "camera sandbox", 800, 600) do return + defer eng.shutdown(&app) + + data, ok := eng.load_character_data(&app, "assets_baked/characters/toad/toad.char.json") + if !ok do return + defer eng.destroy_character_data(&app, &data) + + toad := eng.spawn_sprite(&data, {0, 0}, "idle", 0) + app.camera.position = {0, -100} + + follow := false + space_was_down := false + CAM_SPEED :: f32(300) + + last := eng.now_seconds() + + for eng.events() { + now := eng.now_seconds() + dt := f32(now - last) + last = now + + space := eng.key_down(.Space) + if space && !space_was_down { + follow = !follow + } + space_was_down = space + + if follow { + app.camera.position = toad.position - {0, 100} + } else { + if eng.key_down(.Left) || eng.key_down(.A) { + app.camera.position.x -= CAM_SPEED * dt + } + if eng.key_down(.Right) || eng.key_down(.D) { + app.camera.position.x += CAM_SPEED * dt + } + if eng.key_down(.Up) { + app.camera.position.y -= CAM_SPEED * dt + } + if eng.key_down(.Down) { + app.camera.position.y += CAM_SPEED * dt + } + } + + eng.update_sprite(&toad, dt) + + eng.begin_frame(&app) + eng.draw_sprite(&app, &toad) + eng.end_frame(&app) + } +} diff --git a/examples/clips/main.odin b/examples/clips/main.odin new file mode 100644 index 0000000..cb14b9b --- /dev/null +++ b/examples/clips/main.odin @@ -0,0 +1,38 @@ +package main + +import eng "pkg:engine" + +// Clip switcher: 1 = idle, 2 = walk. No movement — animation policy only. +main :: proc() { + app: eng.App + if !eng.init(&app, "clips", 800, 600) do return + defer eng.shutdown(&app) + + data, ok := eng.load_character_data(&app, "assets_baked/characters/toad/toad.char.json") + if !ok do return + defer eng.destroy_character_data(&app, &data) + + toad := eng.spawn_sprite(&data, {400, 500}, "idle", 0) + app.camera.position = toad.position - {0, 100} + + last := eng.now_seconds() + + for eng.events() { + now := eng.now_seconds() + dt := f32(now - last) + last = now + + if eng.key_down(.N1) { + eng.set_sprite_clip(&toad, "idle") + } + if eng.key_down(.N2) { + eng.set_sprite_clip(&toad, "walk") + } + + eng.update_sprite(&toad, dt) + + eng.begin_frame(&app) + eng.draw_sprite(&app, &toad) + eng.end_frame(&app) + } +} diff --git a/examples/crowd/main.odin b/examples/crowd/main.odin new file mode 100644 index 0000000..42e159d --- /dev/null +++ b/examples/crowd/main.odin @@ -0,0 +1,49 @@ +package main + +import eng "pkg:engine" + +// Many sprites sharing one Character_Data (Flyweight) — good batching demo. +COUNT :: 24 + +main :: proc() { + app: eng.App + if !eng.init(&app, "crowd", 800, 600) do return + defer eng.shutdown(&app) + + data, ok := eng.load_character_data(&app, "assets_baked/characters/toad/toad.char.json") + if !ok do return + defer eng.destroy_character_data(&app, &data) + + sprites: [COUNT]eng.Sprite + for i in 0 ..< COUNT { + col := i % 8 + row := i / 8 + pos := eng.Vec2 { + f32(120 + col * 80), + f32(280 + row * 120), + } + clip := "idle" if (i % 2) == 0 else "walk" + sprites[i] = eng.spawn_sprite(&data, pos, clip, i % 5) + } + + // Look at the middle of the grid + app.camera.position = {400, 400} + + last := eng.now_seconds() + + for eng.events() { + now := eng.now_seconds() + dt := f32(now - last) + last = now + + for &s in sprites { + eng.update_sprite(&s, dt) + } + + eng.begin_frame(&app) + for &s in sprites { + eng.draw_sprite(&app, &s) + } + eng.end_frame(&app) + } +} diff --git a/examples/hello_sprite/main.odin b/examples/hello_sprite/main.odin new file mode 100644 index 0000000..5027eaf --- /dev/null +++ b/examples/hello_sprite/main.odin @@ -0,0 +1,34 @@ +package main + +import eng "pkg:engine" + +// Minimal: load one baked character and draw idle. No input, no camera follow. +main :: proc() { + app: eng.App + if !eng.init(&app, "hello sprite", 800, 600) do return + defer eng.shutdown(&app) + + data, ok := eng.load_character_data(&app, "assets_baked/characters/toad/toad.char.json") + if !ok do return + defer eng.destroy_character_data(&app, &data) + + sprite := eng.spawn_sprite(&data, {400, 500}, "idle", 0) + + // Screen-centered framing without gameplay camera follow: + // treat spawn position as world; look slightly above feet. + app.camera.position = sprite.position - {0, 100} + + last := eng.now_seconds() + + for eng.events() { + now := eng.now_seconds() + dt := f32(now - last) + last = now + + eng.update_sprite(&sprite, dt) + + eng.begin_frame(&app) + eng.draw_sprite(&app, &sprite) + eng.end_frame(&app) + } +}