gutter_runner/game/game.odin
2025-01-02 15:07:57 +04:00

187 lines
4.0 KiB
Odin

// This file is compiled as part of the `odin.dll` file. It contains the
// procs that `game_hot_reload.exe` will call, such as:
//
// game_init: Sets up the game state
// game_update: Run once per frame
// game_shutdown: Shuts down game and frees memory
// game_memory: Run just before a hot reload, so game.exe has a pointer to the
// game's memory.
// game_hot_reloaded: Run after a hot reload so that the `g_mem` global variable
// can be set to whatever pointer it was in the old DLL.
//
// Note: When compiled as part of the release executable this whole package is imported as a normal
// odin package instead of a DLL.
package game
import "core:fmt"
import "core:math/linalg"
import rl "vendor:raylib"
PIXEL_WINDOW_HEIGHT :: 360
Game_Memory :: struct {
player_pos: rl.Vector3,
camera_yaw_pitch: rl.Vector2,
camera_speed: f32,
mouse_captured: bool,
}
g_mem: ^Game_Memory
game_camera :: proc() -> rl.Camera2D {
w := f32(rl.GetScreenWidth())
h := f32(rl.GetScreenHeight())
return {zoom = h / PIXEL_WINDOW_HEIGHT, target = g_mem.player_pos.xy, offset = {w / 2, h / 2}}
}
camera_rotation_matrix :: proc() -> matrix[3, 3]f32 {
return linalg.matrix3_from_euler_angles_xy(g_mem.camera_yaw_pitch.x, g_mem.camera_yaw_pitch.y)
}
camera_forward_vec :: proc() -> rl.Vector3 {
rotation_matrix := camera_rotation_matrix()
return rotation_matrix * rl.Vector3{0, 0, 1}
}
game_camera_3d :: proc() -> rl.Camera3D {
return {
position = g_mem.player_pos,
up = {0, 1, 0},
fovy = 60,
target = g_mem.player_pos + camera_forward_vec(),
projection = .PERSPECTIVE,
}
}
ui_camera :: proc() -> rl.Camera2D {
return {zoom = f32(rl.GetScreenHeight()) / PIXEL_WINDOW_HEIGHT}
}
update_free_look_camera :: proc() {
input: rl.Vector2
if rl.IsKeyDown(.UP) || rl.IsKeyDown(.W) {
input.y -= 1
}
if rl.IsKeyDown(.DOWN) || rl.IsKeyDown(.S) {
input.y += 1
}
if rl.IsKeyDown(.LEFT) || rl.IsKeyDown(.A) {
input.x -= 1
}
if rl.IsKeyDown(.RIGHT) || rl.IsKeyDown(.D) {
input.x += 1
}
if rl.IsKeyPressed(.ESCAPE) {
if g_mem.mouse_captured {
g_mem.mouse_captured = false
rl.EnableCursor()
}
}
if g_mem.mouse_captured {
g_mem.camera_yaw_pitch += rl.GetMouseDelta().yx * -1 * 0.001
}
g_mem.camera_speed += rl.GetMouseWheelMove() * 0.01
g_mem.camera_speed = linalg.clamp(g_mem.camera_speed, 0.01, 10)
rotation_matrix := camera_rotation_matrix()
forward := -rotation_matrix[2]
right := linalg.cross(rl.Vector3{0, 1, 0}, forward)
input = linalg.normalize0(input)
g_mem.player_pos += (input.x * right + input.y * forward) * g_mem.camera_speed
}
update :: proc() {
if rl.IsMouseButtonPressed(.LEFT) {
g_mem.mouse_captured = true
rl.DisableCursor()
}
update_free_look_camera()
}
draw :: proc() {
rl.BeginDrawing()
rl.ClearBackground(rl.BLACK)
{
rl.BeginMode3D(game_camera_3d())
defer rl.EndMode3D()
rl.DrawBoundingBox(rl.BoundingBox{min = -1, max = 1}, {255, 0, 0, 255})
}
rl.BeginMode2D(ui_camera())
// Note: main_hot_reload.odin clears the temp allocator at end of frame.
rl.DrawText(fmt.ctprintf("player_pos: %v", g_mem.player_pos), 5, 5, 8, rl.WHITE)
rl.EndMode2D()
rl.EndDrawing()
}
@(export)
game_update :: proc() -> bool {
update()
draw()
return !rl.WindowShouldClose()
}
@(export)
game_init_window :: proc() {
rl.SetConfigFlags({.WINDOW_RESIZABLE, .VSYNC_HINT})
rl.InitWindow(1280, 720, "Odin + Raylib + Hot Reload template!")
rl.SetExitKey(.KEY_NULL)
rl.SetWindowPosition(200, 200)
rl.SetTargetFPS(500)
}
@(export)
game_init :: proc() {
g_mem = new(Game_Memory)
g_mem^ = Game_Memory{}
game_hot_reloaded(g_mem)
}
@(export)
game_shutdown :: proc() {
free(g_mem)
}
@(export)
game_shutdown_window :: proc() {
rl.CloseWindow()
}
@(export)
game_memory :: proc() -> rawptr {
return g_mem
}
@(export)
game_memory_size :: proc() -> int {
return size_of(Game_Memory)
}
@(export)
game_hot_reloaded :: proc(mem: rawptr) {
g_mem = (^Game_Memory)(mem)
}
@(export)
game_force_reload :: proc() -> bool {
return rl.IsKeyPressed(.F5)
}
@(export)
game_force_restart :: proc() -> bool {
return rl.IsKeyPressed(.F6)
}