Consolidated scenes for neatness.
This commit is contained in:
+17
-12
@@ -10,10 +10,8 @@ WINDOW_TITLE :: "Pong"
|
||||
|
||||
Game :: struct {
|
||||
viewport: rl.RenderTexture2D,
|
||||
// base scene (gameplay, mainmenu)
|
||||
scene: Scene,
|
||||
// active overlay (pause, settings, or nil)
|
||||
overlay_scene: Maybe(Overlay_Scene),
|
||||
overlay_scene: Maybe(Scene),
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
@@ -24,18 +22,21 @@ game_init :: proc(game: ^Game) {
|
||||
rl.SetTargetFPS(60)
|
||||
|
||||
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) {
|
||||
dt := rl.GetFrameTime()
|
||||
input := poll_input()
|
||||
|
||||
// update the overlay if active, otherwise update base scene
|
||||
if game.overlay_scene != nil {
|
||||
overlay_update(game, input, dt)
|
||||
// Pass direct pointers (^Scene) so modifications persist
|
||||
if overlay, ok := &game.overlay_scene.?; ok {
|
||||
scene_update(overlay, game, input, dt)
|
||||
} 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.ClearBackground(rl.BLACK)
|
||||
|
||||
// offscreen pass render everything into virtual 1280x720 target
|
||||
rl.BeginTextureMode(game.viewport)
|
||||
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()
|
||||
|
||||
// upscale pass stretch virtual buffer onto physical window display
|
||||
render_game_to_window(game.viewport.texture)
|
||||
|
||||
rl.EndDrawing()
|
||||
|
||||
+112
-75
@@ -3,98 +3,135 @@ package game
|
||||
import rl "vendor:raylib"
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Scene & Overlay Definitions
|
||||
// Scene Definitions
|
||||
// ------------------------------------------------------------------------------
|
||||
Scene_Gameplay :: struct {
|
||||
level: Level,
|
||||
Scene_Type :: enum {
|
||||
Main_Menu,
|
||||
Gameplay,
|
||||
Pause_Menu,
|
||||
Game_Over,
|
||||
}
|
||||
|
||||
Scene_Menu :: struct {
|
||||
// @todo: ui_canvas layout state
|
||||
}
|
||||
|
||||
Scene_PauseMenu :: struct {
|
||||
// @todo: pause option state
|
||||
}
|
||||
|
||||
Scene :: union {
|
||||
Scene_Menu,
|
||||
Scene_Gameplay,
|
||||
}
|
||||
|
||||
Overlay_Scene :: union {
|
||||
Scene_PauseMenu,
|
||||
Scene :: struct {
|
||||
type: Scene_Type,
|
||||
level: Maybe(Level),
|
||||
canvas: Maybe(UI_Canvas),
|
||||
winner_is_player: bool, // Store win state for rendering
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Base Scene Dispatchers
|
||||
// UI Definitions
|
||||
// ------------------------------------------------------------------------------
|
||||
scene_update :: proc(game: ^Game, input: Input_Event, dt: f32) {
|
||||
switch &s in game.scene {
|
||||
case Scene_Menu: // update_menu(&s, game, input, dt)
|
||||
case Scene_Gameplay: update_gameplay(&s, game, input, dt)
|
||||
}
|
||||
}
|
||||
|
||||
scene_render :: proc(game: ^Game) {
|
||||
switch &s in game.scene {
|
||||
case Scene_Menu: // render_menu(s)
|
||||
case Scene_Gameplay: render_gameplay(s)
|
||||
}
|
||||
UI_Canvas :: struct {
|
||||
bounds: rl.Rectangle,
|
||||
padding: f32,
|
||||
item_height: f32,
|
||||
item_spaceing: f32,
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------
|
||||
// Overlay Scene Dispatchers
|
||||
// Scene Constructors
|
||||
// ------------------------------------------------------------------------------
|
||||
overlay_update :: proc(game: ^Game, input: Input_Event, dt: f32) -> bool {
|
||||
overlay := game.overlay_scene.? or_return
|
||||
|
||||
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{
|
||||
make_gameplay_scene :: proc() -> Scene {
|
||||
return Scene{
|
||||
type = .Gameplay,
|
||||
level = init_level(),
|
||||
}
|
||||
}
|
||||
|
||||
update_gameplay :: proc(scene: ^Scene_Gameplay, game: ^Game, input: Input_Event, dt: f32) {
|
||||
if input.pause_pressed {
|
||||
game.overlay_scene = Scene_PauseMenu{}
|
||||
return
|
||||
make_pause_overlay :: proc() -> Scene {
|
||||
return Scene{
|
||||
type = .Pause_Menu,
|
||||
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) {
|
||||
render_level(scene.level)
|
||||
render_hud(scene.level)
|
||||
scene_render :: proc(scene: ^Scene) {
|
||||
// render gameplay field & hud if present
|
||||
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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user