renamed scene manager.

This commit is contained in:
2026-09-09 07:18:08 +02:00
parent d453a2cb33
commit 8f91f0ea5d
2 changed files with 155 additions and 132 deletions
-132
View File
@@ -1,132 +0,0 @@
package game
import rl "vendor:raylib"
Scene :: union {
Scene_Menu,
Scene_Gameplay,
}
Scene_Menu :: struct {
selected_option: int,
}
Scene_Gameplay :: struct {
level: Level,
}
// ------------------------------------------------------------------------------
// Scene Dispatchers
// ------------------------------------------------------------------------------
scene_update :: proc(game: ^Game, dt: f32) {
// Must mutate the actual union via pointer
switch &s in game.current_scene {
case Scene_Menu: // update_menu(&s, game, dt)
case Scene_Gameplay: update_gameplay(&s, game, dt)
}
}
scene_render :: proc(game: ^Game) {
// Value inspect is fine for rendering
switch &s in game.current_scene {
case Scene_Menu: // render_menu(s)
case Scene_Gameplay: render_gameplay(s, game.is_paused)
}
}
// ------------------------------------------------------------------------------
// Gameplay Scene
// ------------------------------------------------------------------------------
init_gameplay_scene :: proc() -> Scene_Gameplay {
return Scene_Gameplay{
level = init_level(),
}
}
update_gameplay :: proc(scene: ^Scene_Gameplay, game: ^Game, dt: f32) {
if rl.IsKeyPressed(.P) {
game.is_paused = !game.is_paused
}
if game.is_paused {
if rl.IsKeyPressed(.Q) {
game.current_scene = Scene_Menu{selected_option = 0}
}
return
}
// Delegate world updates to level logic
update_level(&scene.level, dt)
}
render_gameplay :: proc(scene: Scene_Gameplay, is_paused: bool) {
// Delegate world rendering to level logic
render_level(scene.level)
// Scene UI Overlays
if is_paused {
rl.DrawText("PAUSED", 1200, 700, 40, rl.WHITE)
rl.DrawText("Press Q to Quit to Menu", 1150, 760, 20, rl.LIGHTGRAY)
}
}
//
Level :: struct {
field_bounds: rl.Rectangle,
player: Entity,
enemy: Entity,
ball: Entity,
}
init_level :: proc() -> Level {
pad_x := f32(WINDOW_WIDTH) * 0.10
pad_y := f32(WINDOW_HEIGHT) * 0.10
bounds := rl.Rectangle{
x = pad_x,
y = pad_y,
width = f32(WINDOW_WIDTH) - (pad_x * 2),
height = f32(WINDOW_HEIGHT) - (pad_y * 2),
}
// player entity
player_ent := init_player()
player_ent.pos.x = bounds.x + 3
player_ent.pos.y = bounds.y + (bounds.height / 2) - (player_ent.shape_size.y / 2)
// enemy entity
enemy_ent := init_enemy()
enemy_ent.pos.x = (bounds.width + pad_x) - (enemy_ent.shape_size.x) - 3
enemy_ent.pos.y = bounds.y + (bounds.height / 2) - (enemy_ent.shape_size.y / 2)
return Level{
field_bounds = bounds,
player = player_ent,
enemy = enemy_ent,
ball = init_ball()
}
}
update_level :: proc(level: ^Level, dt: f32) {
update_player(dt, &level.player, level)
// update_enemy(...)
// update_ball(...)
}
render_level :: proc(level: Level) {
// Field Background & Outer Border
rl.DrawRectangleRec(level.field_bounds, rl.DARKGRAY)
rl.DrawRectangleLinesEx(level.field_bounds, 3, rl.RAYWHITE)
// Center Divide Line
center_x := f32(WINDOW_WIDTH) / 2.0
start_pos := rl.Vector2{center_x, level.field_bounds.y + 3}
end_pos := rl.Vector2{center_x, level.field_bounds.y + level.field_bounds.height - 3}
rl.DrawLineEx(start_pos, end_pos, 3.0, rl.WHITE)
// Entities
render_player(level.player, level.field_bounds)
render_enemy(level.enemy, level.field_bounds)
render_ball(level.ball, level.field_bounds)
}
+155
View File
@@ -0,0 +1,155 @@
package game
import rl "vendor:raylib"
Scene :: union {
Scene_Menu,
Scene_Gameplay,
}
Scene_Menu :: struct {
selected_option: int,
}
Scene_Gameplay :: struct {
level: Level,
}
// ------------------------------------------------------------------------------
// Scene Dispatchers
// ------------------------------------------------------------------------------
scene_update :: proc(game: ^Game, input: Input_Event, dt: f32) {
switch &s in game.scene {
case Scene_Menu: // update_menu(&s, game, 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, game.viewport, game.paused)
}
}
// ------------------------------------------------------------------------------
// Menu Scene
// ------------------------------------------------------------------------------
// @TODO:
// update_menu :: proc
// if input.move_dir.y < 0 { // UP
// menu.selected_option = max(0, menu.selected_option - 1)
// } else if input.move_dir.y > 0 { // DOWN
// menu.selected_option = min(MAX_OPTIONS - 1, menu.selected_option + 1)
// }
// if input.confirm_pressed {
// // Execute menu selection
// }
// ------------------------------------------------------------------------------
// Gameplay Scene
// ------------------------------------------------------------------------------
init_gameplay_scene :: proc() -> Scene_Gameplay {
return Scene_Gameplay{
level = init_level(),
}
}
update_gameplay :: proc(scene: ^Scene_Gameplay, game: ^Game, input: Input_Event, dt: f32) {
if input.pause_pressed {
game.paused = !game.paused
}
if game.paused do return
update_level(&scene.level, input, dt)
}
render_gameplay :: proc(scene: Scene_Gameplay, viewport_texture: rl.RenderTexture2D, is_paused: bool) {
// Offscreen Pass: Render all game objects at low/fixed virtual resolution (1280x720)
rl.BeginTextureMode(viewport_texture)
rl.ClearBackground(rl.BLACK)
render_level(scene.level)
if is_paused {
// UI coordinates aligned relative to internal 1280x720 viewport space
rl.DrawRectangleRec(rl.Rectangle{0,0, VIEWPORT_WIDTH, VIEWPORT_HEIGHT}, rl.Color{0,0,0,140})
rl.DrawText("PAUSED", VIEWPORT_WIDTH / 2 - 80, VIEWPORT_HEIGHT / 2 - 40, 40, rl.WHITE)
rl.DrawText("Press Q to Quit to Menu", VIEWPORT_WIDTH / 2 - 110, VIEWPORT_HEIGHT / 2 + 10, 20, rl.LIGHTGRAY)
}
rl.EndTextureMode()
// Upscale Pass: Stretch render buffer onto actual window display
render_game_to_window(viewport_texture.texture)
}
render_game_to_window :: proc(texture: rl.Texture) {
rl.DrawTexturePro(
texture,
// Source rect: OpenGL render targets have inverted Y coordinates (-height flips it right side up)
rl.Rectangle{0, 0, f32(texture.width), -f32(texture.height)},
// Dest rect: Scales 1280x720 canvas to fill window resolution (2560x1440)
rl.Rectangle{0, 0, f32(WINDOW_WIDTH), f32(WINDOW_HEIGHT)},
rl.Vector2{},
0,
rl.WHITE
)
}
// ------------------------------------------------------------------------------
// LEVEL specifics
// ------------------------------------------------------------------------------
Level :: struct {
field_bounds: rl.Rectangle,
player: Entity,
enemy: Entity,
ball: Entity,
}
init_level :: proc() -> Level {
// Positioning based strictly on internal viewport resolution, independent of target display size
pad_x := f32(VIEWPORT_WIDTH) * 0.10
pad_y := f32(VIEWPORT_HEIGHT) * 0.10
bounds := rl.Rectangle{
x = pad_x,
y = pad_y,
width = f32(VIEWPORT_WIDTH) - (pad_x * 2),
height = f32(VIEWPORT_HEIGHT) - (pad_y * 2),
}
player_ent := init_player()
player_ent.pos.x = bounds.x + 3
player_ent.pos.y = bounds.y + (bounds.height / 2) - (player_ent.size.y / 2)
enemy_ent := init_enemy()
enemy_ent.pos.x = (bounds.x + bounds.width) - (enemy_ent.size.x) - 3
enemy_ent.pos.y = bounds.y + (bounds.height / 2) - (enemy_ent.size.y / 2)
return Level{
field_bounds = bounds,
player = player_ent,
enemy = enemy_ent,
ball = init_ball(),
}
}
update_level :: proc(level: ^Level, input: Input_Event, dt: f32) {
update_player(&level.player, input, level.field_bounds, dt)
update_ball(&level.ball, level.field_bounds, dt)
}
render_level :: proc(level: Level) {
rl.DrawRectangleRec(level.field_bounds, rl.DARKGRAY)
rl.DrawRectangleLinesEx(level.field_bounds, 3, rl.RAYWHITE)
center_x := f32(VIEWPORT_WIDTH) / 2.0
start_pos := rl.Vector2{center_x, level.field_bounds.y + 3}
end_pos := rl.Vector2{center_x, level.field_bounds.y + level.field_bounds.height - 3}
rl.DrawLineEx(start_pos, end_pos, 3.0, rl.WHITE)
render_player(level.player, level.field_bounds)
render_enemy(level.enemy, level.field_bounds)
render_ball(level.ball, level.field_bounds)
}