untitled-roguelike/source/game.odin
2026-05-03 02:09:13 -07:00

173 lines
4.1 KiB
Odin

/*
This file is the starting point of your game.
Some important procedures are:
- game_init_window: Opens the window
- game_init: Sets up the game state
- game_update: Run once per frame
- game_should_close: For stopping your game when close button is pressed
- game_shutdown: Shuts down game and frees memory
- game_shutdown_window: Closes window
The procs above are used regardless if you compile using the `build_release`
script or the `build_hot_reload` script. However, in the hot reload case, the
contents of this file is compiled as part of `build/hot_reload/game.dll` (or
.dylib/.so on mac/linux). In the hot reload cases some other procedures are
also used in order to facilitate the hot reload functionality:
- game_memory: Run just before a hot reload. That way game_hot_reload.exe has a
pointer to the game's memory that it can hand to the new game DLL.
- game_hot_reloaded: Run after a hot reload so that the `g` global
variable can be set to whatever pointer it was in the old DLL.
NOTE: When compiled as part of `build_release`, `build_debug` or `build_web`
then this whole package is just treated as a normal Odin package. No DLL is
created.
*/
// NOTE: `fmt.ctprintf` uses the temp allocator. The temp allocator is
// cleared at the end of the frame by the main application, meaning inside
// `main_hot_reload.odin`, `main_release.odin` or `main_web_entry.odin`.
package game
import "engine"
import rl "vendor:raylib"
GAME_WIDTH :: 640
GAME_HEIGHT :: 270
Game_Memory :: struct {
render_target: rl.RenderTexture,
render_scale: f32,
run: bool,
}
g: ^Game_Memory
update :: proc() {
if rl.IsKeyPressed(.ESCAPE) {
g.run = false
}
g.render_scale = min(
f32(rl.GetScreenWidth()) / f32(GAME_WIDTH),
f32(rl.GetScreenHeight()) / f32(GAME_HEIGHT),
)
rl.SetMouseOffset(
-i32((f32(rl.GetScreenWidth()) - (f32(GAME_WIDTH) * g.render_scale)) * 0.5),
-i32((f32(rl.GetScreenHeight()) - (f32(GAME_HEIGHT) * g.render_scale)) * 0.5),
)
rl.SetMouseScale(1 / g.render_scale, 1 / g.render_scale)
engine.update()
}
draw :: proc() {
rl.BeginTextureMode(g.render_target)
rl.ClearBackground(rl.BLACK)
engine.draw()
rl.EndTextureMode()
rl.BeginDrawing()
rl.ClearBackground(rl.BLACK)
rl.DrawTexturePro(
g.render_target.texture,
{0, 0, f32(g.render_target.texture.width), f32(-g.render_target.texture.height)},
{
(f32(rl.GetScreenWidth()) - (f32(GAME_WIDTH) * g.render_scale)) * 0.5,
(f32(rl.GetScreenHeight()) - (f32(GAME_HEIGHT) * g.render_scale)) * 0.5,
f32(GAME_WIDTH) * g.render_scale,
f32(GAME_HEIGHT) * g.render_scale,
},
{0, 0},
0,
rl.WHITE,
)
rl.EndDrawing()
}
@(export)
game_update :: proc() {
update()
draw()
free_all(context.temp_allocator)
}
@(export)
game_init_window :: proc() {
rl.SetConfigFlags({.WINDOW_RESIZABLE, .VSYNC_HINT})
rl.InitWindow(1280, 720, "Untitle Roguelike")
rl.SetWindowPosition(200, 200)
rl.SetTargetFPS(60)
rl.SetExitKey(nil)
}
@(export)
game_init :: proc() {
g = new(Game_Memory)
g^ = Game_Memory {
render_target = rl.LoadRenderTexture(GAME_WIDTH, GAME_HEIGHT),
run = true,
}
engine.init()
game_hot_reloaded(g)
}
@(export)
game_should_run :: proc() -> bool {
when ODIN_OS != .JS {
if rl.WindowShouldClose() {
return false
}
}
return g.run
}
@(export)
game_shutdown :: proc() {
free(g)
engine.unload()
}
@(export)
game_shutdown_window :: proc() {
rl.CloseWindow()
}
@(export)
game_memory :: proc() -> rawptr {
return g
}
@(export)
game_memory_size :: proc() -> int {
return size_of(Game_Memory)
}
@(export)
game_hot_reloaded :: proc(mem: rawptr) {
g = (^Game_Memory)(mem)
// Here you can also set your own global variables. A good idea is to make
// your global variables into pointers that point to something inside `g`.
}
@(export)
game_force_reload :: proc() -> bool {
return rl.IsKeyPressed(.F5)
}
@(export)
game_force_restart :: proc() -> bool {
return rl.IsKeyPressed(.F6)
}
// In a web build, this is called when browser changes size. Remove the
// `rl.SetWindowSize` call if you don't want a resizable game.
game_parent_window_size_changed :: proc(w, h: int) {
rl.SetWindowSize(i32(w), i32(h))
}