/* 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)) }