Menu, tweens, fixed ball bugs
This commit is contained in:
parent
91f72163c3
commit
0288abeb63
Binary file not shown.
|
@ -37,13 +37,14 @@ ball_update :: proc(ball: ^Ball, game: ^Game, delta: f32) -> bool {
|
||||||
ball.velocity.x = -ball.velocity.x
|
ball.velocity.x = -ball.velocity.x
|
||||||
ball.angular_velocity = 0
|
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.position.y = ball.radius
|
||||||
ball.velocity.y = -ball.velocity.y
|
ball.velocity.y = -ball.velocity.y
|
||||||
ball.angular_velocity = 0
|
ball.angular_velocity = 0
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ball.position.y + ball.radius > pad.position.y - pad.size.y / 2 &&
|
if ball.position.y + ball.radius > pad.position.y - pad.size.y / 2 &&
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
67
game.odin
67
game.odin
|
@ -4,53 +4,29 @@ import rl "vendor:raylib"
|
||||||
import "vendor:raylib/rlgl"
|
import "vendor:raylib/rlgl"
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:math"
|
import "core:math"
|
||||||
|
import "core:math/ease"
|
||||||
|
|
||||||
// Virtual game field dimensions
|
// Virtual game field dimensions
|
||||||
GameField := Vec2{800, 600}
|
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 {
|
Game :: struct {
|
||||||
using state: GameState,
|
using state: GameState,
|
||||||
lives: u8,
|
lives: u8,
|
||||||
pad: Pad,
|
pad: Pad,
|
||||||
balls: [dynamic]Ball,
|
balls: [dynamic]Ball,
|
||||||
bricks: [dynamic]Brick,
|
bricks: [dynamic]Brick,
|
||||||
camera: rl.Camera2D
|
camera: rl.Camera2D,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
game_init :: proc() -> ^GameState {
|
game_init :: proc(prev: ^GameState = nil) -> ^GameState {
|
||||||
state := new(Game)
|
state := new(Game)
|
||||||
state.variant = state
|
state.variant = state
|
||||||
state.draw = game_draw
|
state.draw = game_draw
|
||||||
state.update = game_update
|
state.update = game_update
|
||||||
|
state.free = game_free
|
||||||
state.lives = 3
|
state.lives = 3
|
||||||
|
state.previous = prev
|
||||||
state.camera = rl.Camera2D{
|
state.camera = rl.Camera2D{
|
||||||
zoom = 1,
|
zoom = 1,
|
||||||
target = GameField / 2,
|
target = GameField / 2,
|
||||||
|
@ -67,6 +43,7 @@ game_init :: proc() -> ^GameState {
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
offset : f32 = 0
|
offset : f32 = 0
|
||||||
for y : f32 = 40; y < GameField.y / 2; y += 35 {
|
for y : f32 = 40; y < GameField.y / 2; y += 35 {
|
||||||
for x : f32 = 40 + offset; x < GameField.x - 40; x += 40 {
|
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)
|
is_inside := ball_update(&ball, game, delta)
|
||||||
if is_inside { all_balls_outside = false }
|
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)
|
unordered_remove(&balls, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -115,15 +95,8 @@ game_update :: proc(state: ^GameState, delta: f32) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#reverse for &brick, i in bricks {
|
#reverse for &brick, i in bricks {
|
||||||
if !brick.is_flying { continue }
|
brick_update(&brick, camera, delta)
|
||||||
brick.velocity.y += 500 * delta
|
if brick.is_to_free {
|
||||||
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}
|
|
||||||
) {
|
|
||||||
unordered_remove(&bricks, i)
|
unordered_remove(&bricks, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,7 +111,6 @@ game_draw :: proc(state: ^GameState) {
|
||||||
|
|
||||||
rl.BeginMode2D(camera)
|
rl.BeginMode2D(camera)
|
||||||
|
|
||||||
rl.ClearBackground(rl.RAYWHITE)
|
|
||||||
|
|
||||||
|
|
||||||
rl.DrawRectangleGradientV(0, 0, i32(GameField.x), i32(GameField.y), rl.RED, 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("%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()
|
rl.EndMode2D()
|
||||||
|
|
||||||
for i : u8 = 0; i < lives; i += 1 {
|
for i : u8 = 0; i < lives; i += 1 {
|
||||||
rl.DrawCircleV(Vec2{20 + 20 * f32(i), 20}, 10, rl.BLUE)
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
34
main.odin
34
main.odin
|
@ -1,15 +1,22 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import rl "vendor:raylib"
|
import rl "vendor:raylib"
|
||||||
|
import rlgl "vendor:raylib/rlgl"
|
||||||
import "core:slice"
|
import "core:slice"
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
|
|
||||||
|
import "core:math/ease"
|
||||||
|
|
||||||
Vec2 :: [2]f32
|
Vec2 :: [2]f32
|
||||||
Vec2i :: [2]i32
|
Vec2i :: [2]i32
|
||||||
|
|
||||||
WINDOW : Vec2i
|
WINDOW : Vec2i
|
||||||
WINDOWF : Vec2
|
WINDOWF : Vec2
|
||||||
|
|
||||||
|
FontTitle : rl.Font
|
||||||
|
FontUI: rl.Font
|
||||||
|
|
||||||
|
WindowShouldExit := false
|
||||||
|
|
||||||
main :: proc() {
|
main :: proc() {
|
||||||
rl.SetConfigFlags(rl.ConfigFlags{.FULLSCREEN_MODE, .VSYNC_HINT, .WINDOW_MAXIMIZED, .WINDOW_UNDECORATED})
|
rl.SetConfigFlags(rl.ConfigFlags{.FULLSCREEN_MODE, .VSYNC_HINT, .WINDOW_MAXIMIZED, .WINDOW_UNDECORATED})
|
||||||
|
@ -17,6 +24,18 @@ main :: proc() {
|
||||||
rl.InitWindow(0, 0, "SinePong")
|
rl.InitWindow(0, 0, "SinePong")
|
||||||
rl.SetTargetFPS(9999)
|
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.x = rl.GetScreenWidth()
|
||||||
WINDOW.y = rl.GetScreenHeight()
|
WINDOW.y = rl.GetScreenHeight()
|
||||||
WINDOWF = Vec2{f32(WINDOW.x), f32(WINDOW.y)}
|
WINDOWF = Vec2{f32(WINDOW.x), f32(WINDOW.y)}
|
||||||
|
@ -24,15 +43,16 @@ main :: proc() {
|
||||||
stack_init()
|
stack_init()
|
||||||
|
|
||||||
defer {
|
defer {
|
||||||
for s, i in state_stack {
|
for state, i in state_stack {
|
||||||
free(s)
|
state->free()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
game := game_init()
|
game := game_init()
|
||||||
append(&state_stack, game)
|
menu := menu_init()
|
||||||
|
append(&state_stack, menu)
|
||||||
|
|
||||||
for (!rl.WindowShouldClose()) {
|
for (!WindowShouldExit) {
|
||||||
if rl.IsWindowResized() {
|
if rl.IsWindowResized() {
|
||||||
WINDOW.x = rl.GetScreenWidth()
|
WINDOW.x = rl.GetScreenWidth()
|
||||||
WINDOW.y = rl.GetScreenHeight()
|
WINDOW.y = rl.GetScreenHeight()
|
||||||
|
@ -42,17 +62,17 @@ main :: proc() {
|
||||||
current_state := state_stack[len(state_stack)-1]
|
current_state := state_stack[len(state_stack)-1]
|
||||||
|
|
||||||
delta := rl.GetFrameTime()
|
delta := rl.GetFrameTime()
|
||||||
|
tweens_process(delta)
|
||||||
|
|
||||||
current_state->update(delta)
|
current_state->update(delta)
|
||||||
|
|
||||||
{
|
{
|
||||||
rl.BeginDrawing()
|
rl.BeginDrawing()
|
||||||
defer rl.EndDrawing()
|
defer rl.EndDrawing()
|
||||||
|
rl.ClearBackground(rl.Color{40, 10, 90, 255})
|
||||||
current_state->draw()
|
current_state->draw()
|
||||||
|
rlgl.PopMatrix()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
95
menu.odin
95
menu.odin
|
@ -1,8 +1,103 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import rl "vendor:raylib"
|
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 {
|
Menu :: struct {
|
||||||
using state: GameState,
|
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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
|
@ -2,11 +2,13 @@ package main
|
||||||
|
|
||||||
import "core:slice"
|
import "core:slice"
|
||||||
|
|
||||||
StateVariant :: union{^Game, ^Menu}
|
StateVariant :: union{^Game, ^Menu, ^Pause}
|
||||||
|
|
||||||
GameState :: struct {
|
GameState :: struct {
|
||||||
update: proc(state: ^GameState, delta: f32),
|
update: proc(state: ^GameState, delta: f32),
|
||||||
draw: proc(state: ^GameState),
|
draw: proc(state: ^GameState),
|
||||||
|
free: proc(state: ^GameState),
|
||||||
|
previous: ^GameState,
|
||||||
|
|
||||||
variant: StateVariant
|
variant: StateVariant
|
||||||
}
|
}
|
||||||
|
@ -41,6 +43,6 @@ stack_pop :: proc() -> (bool) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
state := pop(&state_stack)
|
state := pop(&state_stack)
|
||||||
free(state)
|
state->free()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue