From 0288abeb638ce88c5782bf71c46e2059da1f7075 Mon Sep 17 00:00:00 2001 From: Nefrace Date: Mon, 9 Sep 2024 00:18:35 +0300 Subject: [PATCH] Menu, tweens, fixed ball bugs --- assets/monogram-extended.ttf | Bin 0 -> 55952 bytes ball.odin | 5 +- brick.odin | 44 +++++++++++++++ game.odin | 67 +++++++++-------------- main.odin | 34 +++++++++--- menu.odin | 95 +++++++++++++++++++++++++++++++++ pause.odin | 100 +++++++++++++++++++++++++++++++++++ state.odin | 6 ++- tween.odin | 81 ++++++++++++++++++++++++++++ 9 files changed, 379 insertions(+), 53 deletions(-) create mode 100644 assets/monogram-extended.ttf create mode 100644 brick.odin create mode 100644 pause.odin create mode 100644 tween.odin diff --git a/assets/monogram-extended.ttf b/assets/monogram-extended.ttf new file mode 100644 index 0000000000000000000000000000000000000000..40f7577c62ecfb44274049b2a3b91d9d7836e73f GIT binary patch literal 55952 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 literal 0 HcmV?d00001 diff --git a/ball.odin b/ball.odin index ccfc951..37c9e58 100644 --- a/ball.odin +++ b/ball.odin @@ -37,13 +37,14 @@ ball_update :: proc(ball: ^Ball, game: ^Game, delta: f32) -> bool { ball.velocity.x = -ball.velocity.x ball.angular_velocity = 0 } + } else { + return false } - if ball.position.y < ball.radius - 20 { + if ball.position.y < ball.radius { ball.position.y = ball.radius ball.velocity.y = -ball.velocity.y ball.angular_velocity = 0 - return false } if ball.position.y + ball.radius > pad.position.y - pad.size.y / 2 && diff --git a/brick.odin b/brick.odin new file mode 100644 index 0000000..1b45062 --- /dev/null +++ b/brick.odin @@ -0,0 +1,44 @@ +package main + +import rl "vendor:raylib" + + + +Brick :: struct { + position: Vec2, + velocity: Vec2, + size: Vec2, + angle: f32, + angular_velocity: f32, + is_flying: bool, + is_fixed: bool, + color: rl.Color, + hits: u8, + is_to_free: bool, +} + +spawn_brick :: proc(position: Vec2, size: Vec2, is_fixed: bool = true, color: rl.Color = rl.RED, hits : u8 = 1) -> Brick { + return Brick { + position = position, + velocity = Vec2{}, + size = Vec2{30,20}, + is_flying = false, + is_fixed = is_fixed, + color = color, + hits = hits, + angle = 0 + } +} + +brick_update :: proc(brick: ^Brick, camera: rl.Camera2D, delta: f32) { + if !brick.is_flying { return } + brick.velocity.y += 500 * delta + brick.position += brick.velocity * delta + brick.angle += brick.angular_velocity * delta + screen_pos := rl.GetWorldToScreen2D(brick.position, camera) + if !rl.CheckCollisionRecs(rl.Rectangle{screen_pos.x - brick.size.x / 2, screen_pos.y - brick.size.y / 2, brick.size.x, brick.size.y}, + rl.Rectangle{0, 0, WINDOWF.x, WINDOWF.y} + ) { + brick.is_to_free = true + } +} diff --git a/game.odin b/game.odin index e1bc7e5..fc0323c 100644 --- a/game.odin +++ b/game.odin @@ -4,53 +4,29 @@ import rl "vendor:raylib" import "vendor:raylib/rlgl" import "core:fmt" import "core:math" +import "core:math/ease" // Virtual game field dimensions GameField := Vec2{800, 600} - -Brick :: struct { - position: Vec2, - velocity: Vec2, - size: Vec2, - angle: f32, - angular_velocity: f32, - is_flying: bool, - is_fixed: bool, - color: rl.Color, - hits: u8, -} - -spawn_brick :: proc(position: Vec2, size: Vec2, is_fixed: bool = true, color: rl.Color = rl.RED, hits : u8 = 1) -> Brick { - return Brick { - position = position, - velocity = Vec2{}, - size = Vec2{30,20}, - is_flying = false, - is_fixed = is_fixed, - color = color, - hits = hits, - angle = 0 - } -} - - Game :: struct { using state: GameState, lives: u8, pad: Pad, balls: [dynamic]Ball, bricks: [dynamic]Brick, - camera: rl.Camera2D + camera: rl.Camera2D, } -game_init :: proc() -> ^GameState { +game_init :: proc(prev: ^GameState = nil) -> ^GameState { state := new(Game) state.variant = state state.draw = game_draw state.update = game_update + state.free = game_free state.lives = 3 + state.previous = prev state.camera = rl.Camera2D{ zoom = 1, target = GameField / 2, @@ -67,6 +43,7 @@ game_init :: proc() -> ^GameState { }) + offset : f32 = 0 for y : f32 = 40; y < GameField.y / 2; y += 35 { for x : f32 = 40 + offset; x < GameField.x - 40; x += 40 { @@ -99,7 +76,10 @@ game_update :: proc(state: ^GameState, delta: f32) { is_inside := ball_update(&ball, game, delta) if is_inside { all_balls_outside = false } - if ball.position.y > GameField.y + 200 { + screen_pos := rl.GetWorldToScreen2D(ball.position, camera) + fmt.println(screen_pos) + if !rl.CheckCollisionCircleRec(ball.position, ball.radius, rl.Rectangle{0, 0, WINDOWF.x, WINDOWF.y} + ) { unordered_remove(&balls, i) } } @@ -115,15 +95,8 @@ game_update :: proc(state: ^GameState, delta: f32) { } #reverse for &brick, i in bricks { - if !brick.is_flying { continue } - brick.velocity.y += 500 * delta - brick.position += brick.velocity * delta - brick.angle += brick.angular_velocity * delta - screen_pos := rl.GetWorldToScreen2D(brick.position, camera) - fmt.println(screen_pos) - if !rl.CheckCollisionRecs(rl.Rectangle{screen_pos.x - brick.size.x / 2, screen_pos.y - brick.size.y / 2, brick.size.x, brick.size.y}, - rl.Rectangle{0, 0, WINDOWF.x, WINDOWF.y} - ) { + brick_update(&brick, camera, delta) + if brick.is_to_free { unordered_remove(&bricks, i) } } @@ -138,7 +111,6 @@ game_draw :: proc(state: ^GameState) { rl.BeginMode2D(camera) - rl.ClearBackground(rl.RAYWHITE) rl.DrawRectangleGradientV(0, 0, i32(GameField.x), i32(GameField.y), rl.RED, rl.RAYWHITE) @@ -154,12 +126,23 @@ game_draw :: proc(state: ^GameState) { } //rl.DrawText(rl.TextFormat("%f\n%f\n%f\n%f", pad.position.x, pad.position.y, pad.velocity.x, pad.velocity.y), 0, 0, 20, rl.BLACK) - rl.DrawText(rl.TextFormat("%d", len(bricks)), 0, 0, 20, rl.BLACK) rl.EndMode2D() for i : u8 = 0; i < lives; i += 1 { rl.DrawCircleV(Vec2{20 + 20 * f32(i), 20}, 10, rl.BLUE) } - + rl.DrawTextEx(FontUI, rl.TextFormat("%d", len(bricks)), {}, f32(48), 2, rl.BLACK) } + + +game_free :: proc(state: ^GameState) { + game := transmute(^Game)state + + delete(game.bricks) + delete(game.balls) + + free(state) +} + + diff --git a/main.odin b/main.odin index 1184396..b7a15c1 100644 --- a/main.odin +++ b/main.odin @@ -1,15 +1,22 @@ package main import rl "vendor:raylib" +import rlgl "vendor:raylib/rlgl" import "core:slice" import "core:fmt" +import "core:math/ease" + Vec2 :: [2]f32 Vec2i :: [2]i32 WINDOW : Vec2i WINDOWF : Vec2 +FontTitle : rl.Font +FontUI: rl.Font + +WindowShouldExit := false main :: proc() { rl.SetConfigFlags(rl.ConfigFlags{.FULLSCREEN_MODE, .VSYNC_HINT, .WINDOW_MAXIMIZED, .WINDOW_UNDECORATED}) @@ -17,6 +24,18 @@ main :: proc() { rl.InitWindow(0, 0, "SinePong") rl.SetTargetFPS(9999) + + tween_init() + defer tween_clean() + + FontUI = rl.LoadFontEx("assets/monogram-extended.ttf", 96, nil, 2048) +// FontUI = rl.LoadFont("assets/monogram-extended.ttf") + FontTitle = rl.LoadFontEx("assets/monogram-extended.ttf", 96*2, nil, 2048) + defer rl.UnloadFont(FontTitle) + defer rl.UnloadFont(FontUI) + rl.SetTextureFilter(FontUI.texture, rl.TextureFilter.POINT) + rl.SetTextureFilter(FontTitle.texture, rl.TextureFilter.POINT) + WINDOW.x = rl.GetScreenWidth() WINDOW.y = rl.GetScreenHeight() WINDOWF = Vec2{f32(WINDOW.x), f32(WINDOW.y)} @@ -24,15 +43,16 @@ main :: proc() { stack_init() defer { - for s, i in state_stack { - free(s) + for state, i in state_stack { + state->free() } } game := game_init() - append(&state_stack, game) + menu := menu_init() + append(&state_stack, menu) - for (!rl.WindowShouldClose()) { + for (!WindowShouldExit) { if rl.IsWindowResized() { WINDOW.x = rl.GetScreenWidth() WINDOW.y = rl.GetScreenHeight() @@ -42,17 +62,17 @@ main :: proc() { current_state := state_stack[len(state_stack)-1] delta := rl.GetFrameTime() + tweens_process(delta) current_state->update(delta) - { rl.BeginDrawing() defer rl.EndDrawing() + rl.ClearBackground(rl.Color{40, 10, 90, 255}) current_state->draw() + rlgl.PopMatrix() } - } - } diff --git a/menu.odin b/menu.odin index 53952df..234f7d0 100644 --- a/menu.odin +++ b/menu.odin @@ -1,8 +1,103 @@ package main import rl "vendor:raylib" +import "core:math/ease" +Menu_Buttons :: enum { + START, + HOW_TO_PLAY, + FULLSCREEN, + EXIT +} + +menu_strings := [Menu_Buttons]cstring { + .START = "Старт", + .HOW_TO_PLAY = "Как играть?", + .FULLSCREEN = "Полный экран", + .EXIT = "Выход" +} + Menu :: struct { using state: GameState, + + menu_pos: Vec2, + menu_element_offset: f32, + active_element: Menu_Buttons, + active_marker: Vec2, + marker_tween: ^Tween, +} + +menu_init :: proc(prev: ^GameState = nil) -> ^GameState { + state := new(Menu) + state.variant = state + state.menu_pos = {300, 300} + state.menu_element_offset = 60 + state.active_marker = state.menu_pos + Vec2{0, f32(state.active_element) * state.menu_element_offset} + state.update = menu_update + state.draw = menu_draw + state.free = menu_free + state.previous = prev + + return state +} + +menu_update :: proc(state: ^GameState, delta: f32) { + menu := transmute(^Menu)state + + prev_element := cast(i8)menu.active_element + cur_element := prev_element + + if rl.IsKeyPressed(rl.KeyboardKey.DOWN) { + cur_element += 1 + } + if rl.IsKeyPressed(rl.KeyboardKey.UP) { + cur_element -= 1 + } + if prev_element != cur_element { + if cur_element < 0 { cur_element = len(Menu_Buttons) -1 } + if cur_element == len(Menu_Buttons) { cur_element = 0 } + menu.active_element = cast(Menu_Buttons)cur_element + if menu.marker_tween != nil { + tween_cancel(menu.marker_tween) + } + menu.marker_tween = tween_to( + &menu.active_marker.y, + menu.menu_pos.y + f32(menu.active_element) * menu.menu_element_offset, + 0.5, + ease.Ease.Back_Out, + ) + } + if rl.IsKeyPressed(rl.KeyboardKey.ENTER) || rl.IsKeyPressed(rl.KeyboardKey.SPACE) { + switch menu.active_element { + case .START: + game := game_init(state) + stack_push(game) + case .FULLSCREEN: + case .HOW_TO_PLAY: + case .EXIT: + WindowShouldExit = true + return + } + } +} + +menu_draw :: proc(state: ^GameState) { + menu := transmute(^Menu)state + + TitleFontSize :: 96 + TitleSpacing :: 3 + TitleText :: "ARKADODGE" + TitleSize := rl.MeasureTextEx(FontTitle, TitleText, TitleFontSize, TitleSpacing) + rl.DrawTextPro(FontTitle, "ARKADODGE", {WINDOWF.x - 50, 50}, {TitleSize.x, 0}, 0, 96, 3, rl.WHITE) + + rl.DrawTextEx(FontUI, ">", menu.active_marker + {-30, 0}, 48, 2, rl.WHITE) + for el, i in menu_strings { + pos := menu.menu_pos + {0, f32(i) * menu.menu_element_offset} + rl.DrawTextEx(FontUI, el, pos, 48, 2, rl.WHITE) + } +} + +menu_free :: proc(state: ^GameState) { + free(state) } diff --git a/pause.odin b/pause.odin new file mode 100644 index 0000000..ad3bef9 --- /dev/null +++ b/pause.odin @@ -0,0 +1,100 @@ +package main + +import rl "vendor:raylib" +import "core:math/ease" + + +Pause_Buttons :: enum { + RESUME, + FULLSCREEN, + EXIT +} + +pause_strings := [Pause_Buttons]cstring { + .RESUME = "Старт", + .FULLSCREEN = "Полный экран", + .EXIT = "Выход" +} + +Pause :: struct { + using state: GameState, + + menu_pos: Vec2, + menu_element_offset: f32, + active_element: Pause_Buttons, + active_marker: Vec2, + marker_tween: ^Tween, +} + +pause_init :: proc(prev: ^GameState = nil) -> ^GameState { + state := new(Pause) + state.variant = state + state.menu_pos = {300, 300} + state.menu_element_offset = 60 + state.active_marker = state.menu_pos + Vec2{0, f32(state.active_element) * state.menu_element_offset} + state.update = menu_update + state.draw = menu_draw + state.free = menu_free + state.previous = prev + + + return state +} + +pause_update :: proc(state: ^GameState, delta: f32) { + menu := transmute(^Pause)state + + prev_element := cast(i8)menu.active_element + cur_element := prev_element + + if rl.IsKeyPressed(rl.KeyboardKey.DOWN) { + cur_element += 1 + } + if rl.IsKeyPressed(rl.KeyboardKey.UP) { + cur_element -= 1 + } + if prev_element != cur_element { + if cur_element < 0 { cur_element = len(Pause_Buttons) -1 } + if cur_element == len(Pause_Buttons) { cur_element = 0 } + menu.active_element = cast(Pause_Buttons)cur_element + if menu.marker_tween != nil { + tween_cancel(menu.marker_tween) + } + menu.marker_tween = tween_to( + &menu.active_marker.y, + menu.menu_pos.y + f32(menu.active_element) * menu.menu_element_offset, + 0.5, + ease.Ease.Back_Out, + ) + } + if rl.IsKeyPressed(rl.KeyboardKey.ENTER) || rl.IsKeyPressed(rl.KeyboardKey.SPACE) { + switch menu.active_element { + case .RESUME: + stack_pop() + case .FULLSCREEN: + case .EXIT: + stack_pop() + stack_pop() + } + } +} + +pause_draw :: proc(state: ^GameState) { + menu := transmute(^Pause)state + + TitleFontSize :: 96 + TitleSpacing :: 3 + TitleText :: "ПАУЗА" + TitleSize := rl.MeasureTextEx(FontTitle, TitleText, TitleFontSize, TitleSpacing) + rl.DrawTextPro(FontTitle, "ARKADODGE", {WINDOWF.x - 50, 50}, {TitleSize.x, 0}, 0, 96, 3, rl.WHITE) + + rl.DrawTextEx(FontUI, ">", menu.active_marker + {-30, 0}, 48, 2, rl.WHITE) + for el, i in menu_strings { + pos := menu.menu_pos + {0, f32(i) * menu.menu_element_offset} + rl.DrawTextEx(FontUI, el, pos, 48, 2, rl.WHITE) + } +} + +pause_free :: proc(state: ^GameState) { + free(state) +} diff --git a/state.odin b/state.odin index 7036481..29cd5b5 100644 --- a/state.odin +++ b/state.odin @@ -2,11 +2,13 @@ package main import "core:slice" -StateVariant :: union{^Game, ^Menu} +StateVariant :: union{^Game, ^Menu, ^Pause} GameState :: struct { update: proc(state: ^GameState, delta: f32), draw: proc(state: ^GameState), + free: proc(state: ^GameState), + previous: ^GameState, variant: StateVariant } @@ -41,6 +43,6 @@ stack_pop :: proc() -> (bool) { return false } state := pop(&state_stack) - free(state) + state->free() return true } diff --git a/tween.odin b/tween.odin new file mode 100644 index 0000000..f0a91a9 --- /dev/null +++ b/tween.odin @@ -0,0 +1,81 @@ +package main + +import "core:math" +import "core:math/ease" +import "core:math/linalg" +import "core:slice" + + +Tween :: struct { + ptr: ^f32, + from: f32, + to: f32, + time: f32, + duration: f32, + ease_type: ease.Ease, + active: bool, + finished: proc(data: rawptr), + data: rawptr +} + + +TWEEN_SIZE :: 128 +tweens_buf: [TWEEN_SIZE]^Tween +tweens : [dynamic]^Tween + +tween_init :: proc() { + tweens = slice.into_dynamic(tweens_buf[:]) +} + +tween_clean :: proc() { + for tween, i in tweens { + free(tween) + } +} + +tween_to :: proc( + value: ^f32, to: f32, duration: f32, + ease: ease.Ease = ease.Ease.Quartic_In_Out, + data: rawptr = nil, + callback: proc(data: rawptr) = nil + ) -> ^Tween { + + tween := new(Tween) + tween.ptr = value + tween.from = value^ + tween.to = to + tween.duration = duration + tween.ease_type = ease + tween.active = true + tween.data = data + tween.finished = callback + + append(&tweens, tween) + return tween +} + +tween_cancel :: proc(t: ^Tween) { + t.active = false +} + +tweens_process :: proc(delta: f32) { + #reverse for tween, i in tweens { + + tween.time += delta + p := clamp(tween.time / tween.duration, 0, 1) + + val := ease.ease(tween.ease_type, p) + tween.ptr^ = math.lerp(tween.from, tween.to, val) + + if tween.time >= tween.duration { + tween.active = false + if tween.finished != nil { + tween.finished(tween.data) + } + } + if !tween.active { + free(tween) + unordered_remove(&tweens, i) + } + } +}