Consolidated scenes for neatness.

This commit is contained in:
2026-09-15 05:41:25 +02:00
parent fb1702190d
commit 0000ca4f12
2 changed files with 131 additions and 89 deletions
+17 -12
View File
@@ -10,10 +10,8 @@ WINDOW_TITLE :: "Pong"
Game :: struct { Game :: struct {
viewport: rl.RenderTexture2D, viewport: rl.RenderTexture2D,
// base scene (gameplay, mainmenu)
scene: Scene, scene: Scene,
// active overlay (pause, settings, or nil) overlay_scene: Maybe(Scene),
overlay_scene: Maybe(Overlay_Scene),
} }
// ------------------------------------------------------------------------------ // ------------------------------------------------------------------------------
@@ -24,18 +22,21 @@ game_init :: proc(game: ^Game) {
rl.SetTargetFPS(60) rl.SetTargetFPS(60)
game.viewport = rl.LoadRenderTexture(VIEWPORT_W, VIEWPORT_H) game.viewport = rl.LoadRenderTexture(VIEWPORT_W, VIEWPORT_H)
game.scene = init_gameplay_scene() game.scene = make_gameplay_scene()
// game.scene = make_main_menu()
debug_init()
} }
game_update :: proc(game: ^Game) { game_update :: proc(game: ^Game) {
dt := rl.GetFrameTime() dt := rl.GetFrameTime()
input := poll_input() input := poll_input()
// update the overlay if active, otherwise update base scene // Pass direct pointers (^Scene) so modifications persist
if game.overlay_scene != nil { if overlay, ok := &game.overlay_scene.?; ok {
overlay_update(game, input, dt) scene_update(overlay, game, input, dt)
} else { } else {
scene_update(game, input, dt) scene_update(&game.scene, game, input, dt)
} }
} }
@@ -43,14 +44,18 @@ game_render :: proc(game: ^Game) {
rl.BeginDrawing() rl.BeginDrawing()
rl.ClearBackground(rl.BLACK) rl.ClearBackground(rl.BLACK)
// offscreen pass render everything into virtual 1280x720 target
rl.BeginTextureMode(game.viewport) rl.BeginTextureMode(game.viewport)
rl.ClearBackground(rl.BLACK) rl.ClearBackground(rl.BLACK)
scene_render(game) // Renders base scene (Level field or Main Menu)
overlay_render(game) // Renders active overlay on top if present // Render base scene
scene_render(&game.scene)
// Render active overlay on top if present
if overlay, ok := &game.overlay_scene.?; ok {
scene_render(overlay)
}
rl.EndTextureMode() rl.EndTextureMode()
// upscale pass stretch virtual buffer onto physical window display
render_game_to_window(game.viewport.texture) render_game_to_window(game.viewport.texture)
rl.EndDrawing() rl.EndDrawing()
+112 -75
View File
@@ -3,98 +3,135 @@ package game
import rl "vendor:raylib" import rl "vendor:raylib"
// ------------------------------------------------------------------------------ // ------------------------------------------------------------------------------
// Scene & Overlay Definitions // Scene Definitions
// ------------------------------------------------------------------------------ // ------------------------------------------------------------------------------
Scene_Gameplay :: struct { Scene_Type :: enum {
level: Level, Main_Menu,
Gameplay,
Pause_Menu,
Game_Over,
} }
Scene_Menu :: struct { Scene :: struct {
// @todo: ui_canvas layout state type: Scene_Type,
} level: Maybe(Level),
canvas: Maybe(UI_Canvas),
Scene_PauseMenu :: struct { winner_is_player: bool, // Store win state for rendering
// @todo: pause option state
}
Scene :: union {
Scene_Menu,
Scene_Gameplay,
}
Overlay_Scene :: union {
Scene_PauseMenu,
} }
// ------------------------------------------------------------------------------ // ------------------------------------------------------------------------------
// Base Scene Dispatchers // UI Definitions
// ------------------------------------------------------------------------------ // ------------------------------------------------------------------------------
scene_update :: proc(game: ^Game, input: Input_Event, dt: f32) { UI_Canvas :: struct {
switch &s in game.scene { bounds: rl.Rectangle,
case Scene_Menu: // update_menu(&s, game, input, dt) padding: f32,
case Scene_Gameplay: update_gameplay(&s, game, input, dt) item_height: f32,
} item_spaceing: f32,
}
scene_render :: proc(game: ^Game) {
switch &s in game.scene {
case Scene_Menu: // render_menu(s)
case Scene_Gameplay: render_gameplay(s)
}
} }
// ------------------------------------------------------------------------------ // ------------------------------------------------------------------------------
// Overlay Scene Dispatchers // Scene Constructors
// ------------------------------------------------------------------------------ // ------------------------------------------------------------------------------
overlay_update :: proc(game: ^Game, input: Input_Event, dt: f32) -> bool { make_gameplay_scene :: proc() -> Scene {
overlay := game.overlay_scene.? or_return return Scene{
type = .Gameplay,
switch &o in overlay {
case Scene_PauseMenu:
if input.pause_pressed {
game.overlay_scene = nil
return true
}
// @todo: handle overlay menu option selection (resume, settings, quit)
}
return true
}
overlay_render :: proc(game: ^Game) -> bool {
overlay := game.overlay_scene.? or_return
switch &o in overlay {
case Scene_PauseMenu:
// Translucent dark tint across virtual canvas space
rl.DrawRectangleRec(rl.Rectangle{0, 0, VIEWPORT_W, VIEWPORT_H}, rl.Color{0, 0, 0, 140})
// Pause Menu UI elements
rl.DrawText("PAUSED", VIEWPORT_W / 2 - 80, VIEWPORT_H / 2 - 40, 40, rl.WHITE)
rl.DrawText("Press Q to Quit to Menu", VIEWPORT_W / 2 - 110, VIEWPORT_H / 2 + 10, 20, rl.LIGHTGRAY)
}
return true
}
// ------------------------------------------------------------------------------
// Gameplay Scene Procedures
// ------------------------------------------------------------------------------
init_gameplay_scene :: proc() -> Scene_Gameplay {
return Scene_Gameplay{
level = init_level(), level = init_level(),
} }
} }
update_gameplay :: proc(scene: ^Scene_Gameplay, game: ^Game, input: Input_Event, dt: f32) { make_pause_overlay :: proc() -> Scene {
if input.pause_pressed { return Scene{
game.overlay_scene = Scene_PauseMenu{} type = .Pause_Menu,
return canvas = UI_Canvas{},
}
}
make_main_menu :: proc() -> Scene {
return Scene{
type = .Main_Menu,
canvas = UI_Canvas{},
}
}
make_game_over_overlay :: proc(winner_is_player: bool) -> Scene {
return Scene{
type = .Game_Over,
canvas = UI_Canvas{},
winner_is_player = winner_is_player,
}
}
// ------------------------------------------------------------------------------
// Unified Scene Dispatchers
// ------------------------------------------------------------------------------
scene_update :: proc(scene: ^Scene, game: ^Game, input: Input_Event, dt: f32) {
// mutate level state through pointer if level exists
if level, ok := &scene.level.?; ok {
update_level(level, input, dt)
} }
update_level(&scene.level, input, dt) // dispatch scene behavior
#partial switch scene.type {
case .Gameplay:
if input.pause_pressed {
game.overlay_scene = make_pause_overlay()
}
case .Pause_Menu:
if input.pause_pressed {
game.overlay_scene = nil
}
case .Game_Over:
if input.pause_pressed { // Press P or your start key to restart
game.overlay_scene = nil
game.scene = make_gameplay_scene() // Reset the base level
}
case .Main_Menu:
// @todo: handle menu selection / start game
}
// Loop over global frame events
for event in GLOBAL_EVENTS {
#partial switch event.type {
case .Game_Over:
game.overlay_scene = make_game_over_overlay(event.winner_is_player)
}
}
} }
render_gameplay :: proc(scene: Scene_Gameplay) { scene_render :: proc(scene: ^Scene) {
render_level(scene.level) // render gameplay field & hud if present
render_hud(scene.level) if level, ok := scene.level.?; ok {
render_level(level)
render_hud(level)
}
// render scene/overlay ui elements
#partial switch scene.type {
case .Pause_Menu:
rl.DrawRectangleRec(
rl.Rectangle{0, 0, f32(VIEWPORT_W), f32(VIEWPORT_H)},
rl.Color{0, 0, 0, 140},
)
rl.DrawText("PAUSED", VIEWPORT_W / 2 - 80, VIEWPORT_H / 2 - 40, 40, rl.WHITE)
rl.DrawText("Press P to Resume", VIEWPORT_W / 2 - 90, VIEWPORT_H / 2 + 10, 20, rl.LIGHTGRAY)
case .Game_Over:
rl.DrawRectangleRec(
rl.Rectangle{0, 0, f32(VIEWPORT_W), f32(VIEWPORT_H)},
rl.Color{0, 0, 0, 200},
)
msg :cstring = scene.winner_is_player ? "YOU WIN!" : "GAME OVER"
text_color := scene.winner_is_player ? rl.GREEN : rl.RED
rl.DrawText(msg, VIEWPORT_W / 2 - 100, VIEWPORT_H / 2 - 40, 40, text_color)
rl.DrawText("Press P to Play Again", VIEWPORT_W / 2 - 110, VIEWPORT_H / 2 + 10, 20, rl.LIGHTGRAY)
case .Main_Menu:
rl.DrawText("PONG", VIEWPORT_W / 2 - 50, VIEWPORT_H / 3, 40, rl.WHITE)
rl.DrawText("Press ENTER to Start", VIEWPORT_W / 2 - 110, VIEWPORT_H / 2, 20, rl.LIGHTGRAY)
}
} }