diff --git a/game/app.odin b/game/app.odin index a13e09d..519bc4c 100644 --- a/game/app.odin +++ b/game/app.odin @@ -7,23 +7,26 @@ run :: proc() { defer rl.CloseWindow() rl.SetTargetFPS(60) + init_ui() + defer destroy_ui() + world := init_world() for !rl.WindowShouldClose() { dt := rl.GetFrameTime() + ui_prepare_frame(&world, dt) update_world(&world, dt) rl.BeginDrawing() rl.ClearBackground(rl.Color{34, 139, 34, 255}) render_world(&world) + render_ui() rl.EndDrawing() } delete(world.path) delete(world.towers) delete(world.events) - } - diff --git a/game/hud.odin b/game/hud.odin index 5c4b6a3..eb9623e 100644 --- a/game/hud.odin +++ b/game/hud.odin @@ -1,11 +1,38 @@ package game +import clay "../clay-odin" import "core:fmt" import rl "vendor:raylib" +UI_BAR_COLOR :: clay.Color{20, 40, 20, 200} +UI_TEXT_COLOR :: clay.Color{240, 240, 230, 255} +UI_GOLD_COLOR :: clay.Color{255, 215, 80, 255} + +ui_text_scratch: [64]u8 + +build_hud :: proc(world: ^World) { + if clay.UI(clay.ID("HudBar"))( + { + layout = { + layoutDirection = .LeftToRight, + sizing = {width = clay.SizingGrow({}), height = clay.SizingFixed(28)}, + padding = clay.PaddingAll(6), + childGap = 8, + childAlignment = {y = .Center}, + }, + backgroundColor = UI_BAR_COLOR, + }, + ) { + gold_text := fmt.bprintf(ui_text_scratch[:], "Gold: %d", world.gold) + clay.Text( + gold_text, + clay.TextElementConfig{textColor = UI_GOLD_COLOR, fontId = UI_FONT_ID, fontSize = 18}, + ) + } +} + render_hud :: proc(world: ^World) { - rl.DrawText(fmt.ctprintf("Gold: %d", world.gold), 10, 10, 22, rl.BLACK) // Gold HUD - rl.DrawText(fmt.ctprintf("Base HP: %d", world.base_health), 10, 32, 22, rl.BLACK) // HP HUD + rl.DrawText(fmt.ctprintf("Base HP: %d", world.base_health), 10, 32, 22, rl.BLACK) rl.DrawText(fmt.ctprintf("Enemies: %d", active_enemy_count(world)), 10, 54, 22, rl.BLACK) rl.DrawText(fmt.ctprintf("Wave: %d / %d", world.wave, MAX_WAVES), 10, 76, 22, rl.BLACK) diff --git a/game/render.odin b/game/render.odin index ad32017..708e026 100644 --- a/game/render.odin +++ b/game/render.odin @@ -5,6 +5,5 @@ render_world :: proc(world: ^World) { render_enemies(world) render_towers(world) render_projectiles(world) - render_hud(world) } diff --git a/game/ui.odin b/game/ui.odin new file mode 100644 index 0000000..95ebe2f --- /dev/null +++ b/game/ui.odin @@ -0,0 +1,43 @@ +package game + +import clay "../clay-odin" +import rl "vendor:raylib" + +ui_memory: [^]u8 +ui_arena: clay.Arena +ui_render_commands: clay.ClayArray(clay.RenderCommand) + +ui_error_handler :: proc "c" (data: clay.ErrorData) { + _ = data +} + +init_ui :: proc() { + ui_register_font() + + min_size := clay.MinMemorySize() + ui_memory = make([^]u8, min_size) + ui_arena = clay.CreateArenaWithCapacityAndMemory(uint(min_size), ui_memory) + clay.Initialize(ui_arena, {f32(SCREEN_W), f32(SCREEN_H)}, {handler = ui_error_handler}) + clay.SetMeasureTextFunction(measure_text, nil) + +} + +destroy_ui :: proc() { + free(ui_memory) +} + +ui_prepare_frame :: proc(world: ^World, dt: f32) { + mouse := rl.GetMousePosition() + clay.SetLayoutDimensions({f32(SCREEN_W), f32(SCREEN_H)}) + clay.SetPointerState({mouse.x, mouse.y}, rl.IsMouseButtonDown(.LEFT)) + clay.UpdateScrollContainers(false, {}, dt) + + clay.BeginLayout() + build_hud(world) + ui_render_commands = clay.EndLayout(dt) +} + +render_ui :: proc() { + clay_raylib_render(&ui_render_commands) +} + diff --git a/game/ui_render.odin b/game/ui_render.odin new file mode 100644 index 0000000..d4fb20a --- /dev/null +++ b/game/ui_render.odin @@ -0,0 +1,69 @@ +package game + +import clay "../clay-odin" +import "core:math" +import "core:strings" +import rl "vendor:raylib" + +UI_FONT_ID :: 0 + +ui_font: rl.Font + +ui_register_font :: proc() { + ui_font = rl.GetFontDefault() +} + +clay_color_to_rl :: proc(c: clay.Color) -> rl.Color { + return {u8(c.r), u8(c.g), u8(c.b), u8(c.a)} +} + +measure_text :: proc "c" ( + text: clay.StringSlice, + config: ^clay.TextElementConfig, + _: rawptr, +) -> clay.Dimensions { + return {width = f32(text.length) * f32(config.fontSize) * 0.55, height = f32(config.fontSize)} +} + +clay_raylib_render :: proc(commands: ^clay.ClayArray(clay.RenderCommand)) { + for i in 0 ..< commands.length { + cmd := clay.RenderCommandArray_Get(commands, i) + bounds := cmd.boundingBox + + #partial switch cmd.commandType { + case .None: + case .Text: + cfg := cmd.renderData.text + text := string(cfg.stringContents.chars[:cfg.stringContents.length]) + cstr := strings.clone_to_cstring(text, context.temp_allocator) + + rl.DrawTextEx( + ui_font, + cstr, + {bounds.x, bounds.y}, + f32(cfg.fontSize), + f32(cfg.letterSpacing), + clay_color_to_rl(cfg.textColor), + ) + case .Rectangle: + cfg := cmd.renderData.rectangle + rl.DrawRectangle( + i32(math.round(bounds.x)), + i32(math.round(bounds.y)), + i32(math.round(bounds.width)), + i32(math.round(bounds.height)), + clay_color_to_rl(cfg.backgroundColor), + ) + case .ScissorStart: + rl.BeginScissorMode( + i32(math.round(bounds.x)), + i32(math.round(bounds.y)), + i32(math.round(bounds.width)), + i32(math.round(bounds.height)), + ) + case .ScissorEnd: + rl.EndScissorMode() + } + } +} +