Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cd1d9ef0e4 | ||
|
|
5f38503b49 | ||
|
|
29c88a67ad | ||
|
|
f345b0a5f4 | ||
|
|
ce142a2d02 | ||
|
|
4a1df958e4 |
@@ -4,6 +4,7 @@ set -e
|
|||||||
# Find the odin binary
|
# Find the odin binary
|
||||||
export PATH="$HOME/.odin:$PATH"
|
export PATH="$HOME/.odin:$PATH"
|
||||||
|
|
||||||
|
|
||||||
SRC_DIR="src"
|
SRC_DIR="src"
|
||||||
OUT_DIR="out"
|
OUT_DIR="out"
|
||||||
OUT_NAME="pong"
|
OUT_NAME="pong"
|
||||||
@@ -68,7 +69,7 @@ if [ "$OPTIMIZED" -eq 1 ]; then
|
|||||||
echo "Building optimized ($SRC_DIR -> $OUT_DIR/$OUT_NAME)..."
|
echo "Building optimized ($SRC_DIR -> $OUT_DIR/$OUT_NAME)..."
|
||||||
odin build "$SRC_DIR" \
|
odin build "$SRC_DIR" \
|
||||||
-out:"$OUT_DIR/$OUT_NAME" \
|
-out:"$OUT_DIR/$OUT_NAME" \
|
||||||
-o:speed \
|
-o:speed u
|
||||||
-no-bounds-check
|
-no-bounds-check
|
||||||
else
|
else
|
||||||
echo "Building debug ($SRC_DIR -> $OUT_DIR/$OUT_NAME)..."
|
echo "Building debug ($SRC_DIR -> $OUT_DIR/$OUT_NAME)..."
|
||||||
@@ -86,9 +87,6 @@ if [ "$BUILD_ONLY" -eq 0 ]; then
|
|||||||
lldb "$OUT_DIR/$OUT_NAME"
|
lldb "$OUT_DIR/$OUT_NAME"
|
||||||
|
|
||||||
elif [ "$VALGRIND" -eq 1 ]; then
|
elif [ "$VALGRIND" -eq 1 ]; then
|
||||||
# --show-leak-kinds=definite,possible reduces tons of noise
|
|
||||||
# still shows the leaks you actually need to fix.
|
|
||||||
# to audit whole process, temporarily switch back to --show-leak-kinds=all
|
|
||||||
valgrind \
|
valgrind \
|
||||||
--leak-check=full \
|
--leak-check=full \
|
||||||
--show-leak-kinds=definite,possible \
|
--show-leak-kinds=definite,possible \
|
||||||
|
|||||||
+6305
-17
File diff suppressed because one or more lines are too long
+11
-7
@@ -4,12 +4,15 @@ import "core:math"
|
|||||||
import "core:math/rand"
|
import "core:math/rand"
|
||||||
import rl "vendor:raylib"
|
import rl "vendor:raylib"
|
||||||
|
|
||||||
|
SPEED_INC :: 30
|
||||||
|
EFFECTS_SPEED :: 300
|
||||||
|
|
||||||
init_ball :: proc() -> Entity {
|
init_ball :: proc() -> Entity {
|
||||||
return Entity{
|
return Entity{
|
||||||
type = .Ball,
|
type = .Ball,
|
||||||
active = true,
|
active = true,
|
||||||
pos = rl.Vector2{(VIEWPORT_W / 2), (VIEWPORT_H / 2)},
|
pos = rl.Vector2{(VIEWPORT_W / 2), (VIEWPORT_H / 2)},
|
||||||
speed = 100,
|
speed = 200,
|
||||||
max_speed = 550,
|
max_speed = 550,
|
||||||
vel = rl.Vector2{0,0},
|
vel = rl.Vector2{0,0},
|
||||||
shape = .Circle,
|
shape = .Circle,
|
||||||
@@ -59,16 +62,17 @@ update_ball :: proc(ball: ^Entity, level: ^Level, dt: f32) {
|
|||||||
level.player.color = rl.Color{255,255,255,255}
|
level.player.color = rl.Color{255,255,255,255}
|
||||||
ball.tween_elapsed = 0.0
|
ball.tween_elapsed = 0.0
|
||||||
resolve_collision(ball, level.player, true)
|
resolve_collision(ball, level.player, true)
|
||||||
|
if ball.speed >= EFFECTS_SPEED {
|
||||||
spawn_max_speed_sparks(&level.particles, level.player, ball.pos.y, true)
|
spawn_max_speed_sparks(&level.particles, level.player, ball.pos.y, true)
|
||||||
|
}
|
||||||
|
|
||||||
// if ball.speed >= ball.max_speed - 100 {
|
|
||||||
// spawn_max_speed_sparks(&level.particles, ball.pos, true)
|
|
||||||
// }
|
|
||||||
} else if check_ball_paddle_collision(ball, level.opponent) {
|
} else if check_ball_paddle_collision(ball, level.opponent) {
|
||||||
level.opponent.color = rl.Color{255,255,255,255}
|
level.opponent.color = rl.Color{255,255,255,255}
|
||||||
ball.tween_elapsed = 0.0
|
ball.tween_elapsed = 0.0
|
||||||
resolve_collision(ball, level.opponent, false)
|
resolve_collision(ball, level.opponent, false)
|
||||||
|
if ball.speed >= EFFECTS_SPEED {
|
||||||
spawn_max_speed_sparks(&level.particles, level.opponent, ball.pos.y, false)
|
spawn_max_speed_sparks(&level.particles, level.opponent, ball.pos.y, false)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
level.opponent.color = rl.Color{255,255,255,0}
|
level.opponent.color = rl.Color{255,255,255,0}
|
||||||
level.player.color = rl.Color{255,255,255,0}
|
level.player.color = rl.Color{255,255,255,0}
|
||||||
@@ -101,7 +105,7 @@ check_ball_paddle_collision :: proc(ball: ^Entity, paddle: Entity) -> bool {
|
|||||||
height = paddle.size.y,
|
height = paddle.size.y,
|
||||||
}
|
}
|
||||||
collided := rl.CheckCollisionCircleRec(ball.pos, ball.size.x, paddle_rec)
|
collided := rl.CheckCollisionCircleRec(ball.pos, ball.size.x, paddle_rec)
|
||||||
if collided do emit_event(.Ball_Collision)
|
if collided do emit_event(Ball_Collision_Data{speed = ball.speed})
|
||||||
return collided
|
return collided
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +119,7 @@ increment_score :: proc(score_side: ^int, max_score: int, player: bool, ball: ^E
|
|||||||
|
|
||||||
increment_speed :: proc(ball: ^Entity) {
|
increment_speed :: proc(ball: ^Entity) {
|
||||||
if ball.speed <= ball.max_speed {
|
if ball.speed <= ball.max_speed {
|
||||||
ball.speed += 45
|
ball.speed += SPEED_INC
|
||||||
ball.vel = rl.Vector2Normalize({ball.vel.x, ball.vel.y}) * ball.speed
|
ball.vel = rl.Vector2Normalize({ball.vel.x, ball.vel.y}) * ball.speed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -148,7 +152,7 @@ resolve_collision :: proc(ball: ^Entity, paddle: Entity, is_player: bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
update_ball_trail :: proc(ball: ^Entity, dt: f32) {
|
update_ball_trail :: proc(ball: ^Entity, dt: f32) {
|
||||||
if ball.speed < 300 {
|
if ball.speed < EFFECTS_SPEED {
|
||||||
ball.trail_count = 0
|
ball.trail_count = 0
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-3
@@ -1,6 +1,7 @@
|
|||||||
package game
|
package game
|
||||||
|
|
||||||
Change_Resolution_Data :: struct { res_dir: int }
|
Change_Resolution_Data :: struct { res_dir: int }
|
||||||
|
Ball_Collision_Data :: struct { speed: f32, }
|
||||||
Game_Over_Data :: struct { winner_is_player: bool }
|
Game_Over_Data :: struct { winner_is_player: bool }
|
||||||
|
|
||||||
Simple_Event :: enum {
|
Simple_Event :: enum {
|
||||||
@@ -14,13 +15,13 @@ Simple_Event :: enum {
|
|||||||
Game_Quit,
|
Game_Quit,
|
||||||
UI_Select,
|
UI_Select,
|
||||||
Score_Change,
|
Score_Change,
|
||||||
Ball_Collision,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Event :: union {
|
Event :: union {
|
||||||
Simple_Event,
|
Simple_Event,
|
||||||
Change_Resolution_Data,
|
Change_Resolution_Data,
|
||||||
Game_Over_Data,
|
Game_Over_Data,
|
||||||
|
Ball_Collision_Data,
|
||||||
}
|
}
|
||||||
|
|
||||||
// global event storage
|
// global event storage
|
||||||
@@ -56,6 +57,18 @@ process_events :: proc(game: ^Game) {
|
|||||||
case Game_Over_Data:
|
case Game_Over_Data:
|
||||||
push_scene(game, make_game_over_overlay(e.winner_is_player))
|
push_scene(game, make_game_over_overlay(e.winner_is_player))
|
||||||
|
|
||||||
|
case Ball_Collision_Data:
|
||||||
|
// normalize the speed between 0.0 -> 1.0
|
||||||
|
// map that value to the camera displacement
|
||||||
|
min_speed: f32 = 200.0
|
||||||
|
max_speed: f32 = 550.0
|
||||||
|
speed_factor := clamp((e.speed - min_speed) / (max_speed - min_speed), 0, 1.0)
|
||||||
|
|
||||||
|
intensity := 0.5 + (speed_factor * 4.5)
|
||||||
|
game_trigger_shake(game, intensity, duration = 0.15)
|
||||||
|
|
||||||
|
audio_play_sfx(&game.audio, .Ball_Collision)
|
||||||
|
|
||||||
case Simple_Event:
|
case Simple_Event:
|
||||||
#partial switch e {
|
#partial switch e {
|
||||||
case .Game_Start:
|
case .Game_Start:
|
||||||
@@ -81,8 +94,6 @@ process_events :: proc(game: ^Game) {
|
|||||||
case .UI_Select:
|
case .UI_Select:
|
||||||
audio_play_sfx(&game.audio, .UI_Select)
|
audio_play_sfx(&game.audio, .UI_Select)
|
||||||
|
|
||||||
case .Ball_Collision:
|
|
||||||
audio_play_sfx(&game.audio, .Ball_Collision)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+53
-6
@@ -1,10 +1,9 @@
|
|||||||
package game
|
package game
|
||||||
|
|
||||||
import "core:strings"
|
import "core:strings"
|
||||||
|
import "core:math/rand"
|
||||||
import rl "vendor:raylib"
|
import rl "vendor:raylib"
|
||||||
|
|
||||||
// VIEWPORT_W :: 1280
|
|
||||||
// VIEWPORT_H :: 720
|
|
||||||
VIEWPORT_W :: 640
|
VIEWPORT_W :: 640
|
||||||
VIEWPORT_H :: 360
|
VIEWPORT_H :: 360
|
||||||
WINDOW_TITLE :: "Pong"
|
WINDOW_TITLE :: "Pong"
|
||||||
@@ -26,10 +25,16 @@ RESOLUTIONS := [?]Resolution{
|
|||||||
Game :: struct {
|
Game :: struct {
|
||||||
viewport: rl.RenderTexture2D,
|
viewport: rl.RenderTexture2D,
|
||||||
bg_texture: rl.Texture2D,
|
bg_texture: rl.Texture2D,
|
||||||
|
crt_shader: rl.Shader,
|
||||||
scene_stack: [dynamic]Scene,
|
scene_stack: [dynamic]Scene,
|
||||||
should_close: bool,
|
should_close: bool,
|
||||||
audio: Audio_Manager,
|
audio: Audio_Manager,
|
||||||
|
|
||||||
|
// Camera state for screen effects / screen shake
|
||||||
|
camera: rl.Camera2D,
|
||||||
|
shake_intensity: f32,
|
||||||
|
shake_timer: f32,
|
||||||
|
|
||||||
// resolution state
|
// resolution state
|
||||||
window_w: i32,
|
window_w: i32,
|
||||||
window_h: i32,
|
window_h: i32,
|
||||||
@@ -65,8 +70,8 @@ game_init :: proc(game: ^Game) {
|
|||||||
game.scene_stack = make([dynamic]Scene, 0, 8)
|
game.scene_stack = make([dynamic]Scene, 0, 8)
|
||||||
game.viewport = rl.LoadRenderTexture(VIEWPORT_W, VIEWPORT_H)
|
game.viewport = rl.LoadRenderTexture(VIEWPORT_W, VIEWPORT_H)
|
||||||
|
|
||||||
// Force nearest neighbor point filtering on the viewport texture
|
// Look into texture filters
|
||||||
rl.SetTextureFilter(game.viewport.texture, .POINT)
|
rl.SetTextureFilter(game.viewport.texture, .BILINEAR)
|
||||||
|
|
||||||
// Generate soft CRT background radial texture once on startup
|
// Generate soft CRT background radial texture once on startup
|
||||||
bg_center := rl.Color{22, 28, 36, 255}
|
bg_center := rl.Color{22, 28, 36, 255}
|
||||||
@@ -74,7 +79,16 @@ game_init :: proc(game: ^Game) {
|
|||||||
bg_img := rl.GenImageGradientRadial(VIEWPORT_W, VIEWPORT_H, 0.3, bg_center, bg_corner)
|
bg_img := rl.GenImageGradientRadial(VIEWPORT_W, VIEWPORT_H, 0.3, bg_center, bg_corner)
|
||||||
game.bg_texture = rl.LoadTextureFromImage(bg_img)
|
game.bg_texture = rl.LoadTextureFromImage(bg_img)
|
||||||
rl.UnloadImage(bg_img) // Free CPU memory
|
rl.UnloadImage(bg_img) // Free CPU memory
|
||||||
// rl.SetTextureFilter(game.bg_texture, .BILINEAR)
|
|
||||||
|
game.crt_shader = rl.LoadShader(nil, "assets/shaders/crt.fs")
|
||||||
|
|
||||||
|
// Setup 2D camera matching the 640x360 viewport center
|
||||||
|
game.camera = rl.Camera2D{
|
||||||
|
target = rl.Vector2{VIEWPORT_W / 2.0, VIEWPORT_H / 2.0},
|
||||||
|
offset = rl.Vector2{VIEWPORT_W / 2.0, VIEWPORT_H / 2.0},
|
||||||
|
rotation = 0.0,
|
||||||
|
zoom = 1.0,
|
||||||
|
}
|
||||||
|
|
||||||
game.should_close = false
|
game.should_close = false
|
||||||
set_root_scene(game, make_main_menu())
|
set_root_scene(game, make_main_menu())
|
||||||
@@ -84,6 +98,28 @@ game_update :: proc(game: ^Game) {
|
|||||||
dt := rl.GetFrameTime()
|
dt := rl.GetFrameTime()
|
||||||
input := poll_input()
|
input := poll_input()
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Update Camera Shake
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
base_center := rl.Vector2{VIEWPORT_W / 2.0, VIEWPORT_H / 2.0}
|
||||||
|
|
||||||
|
if game.shake_timer > 0 {
|
||||||
|
game.shake_timer -= dt
|
||||||
|
|
||||||
|
// Generate random offset within current intensity bounds
|
||||||
|
offset_x := (f32(rand.float32()) * 2.0 - 1.0) * game.shake_intensity
|
||||||
|
offset_y := (f32(rand.float32()) * 2.0 - 1.0) * game.shake_intensity
|
||||||
|
|
||||||
|
game.camera.target = base_center + rl.Vector2{offset_x, offset_y}
|
||||||
|
|
||||||
|
if game.shake_timer <= 0 {
|
||||||
|
game.shake_intensity = 0.0
|
||||||
|
game.camera.target = base_center
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
game.camera.target = base_center
|
||||||
|
}
|
||||||
|
|
||||||
active_scene := get_active_scene(game)
|
active_scene := get_active_scene(game)
|
||||||
if active_scene != nil {
|
if active_scene != nil {
|
||||||
scene_update(active_scene, game, input, dt)
|
scene_update(active_scene, game, input, dt)
|
||||||
@@ -103,12 +139,16 @@ game_render :: proc(game: ^Game) {
|
|||||||
rl.BeginTextureMode(game.viewport)
|
rl.BeginTextureMode(game.viewport)
|
||||||
rl.DrawTexture(game.bg_texture, 0, 0, rl.WHITE)
|
rl.DrawTexture(game.bg_texture, 0, 0, rl.WHITE)
|
||||||
|
|
||||||
|
// World/Scene objects rendered through 2D camera space
|
||||||
|
rl.BeginMode2D(game.camera)
|
||||||
|
|
||||||
// @note: fine for now but probably not a good idea...
|
// @note: fine for now but probably not a good idea...
|
||||||
// Render all scenes in the stack from bottom to top
|
// Render all scenes in the stack from bottom to top
|
||||||
for i in 0 ..< len(game.scene_stack) {
|
for i in 0 ..< len(game.scene_stack) {
|
||||||
scene_render(&game.scene_stack[i])
|
scene_render(&game.scene_stack[i])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rl.EndMode2D()
|
||||||
rl.EndTextureMode()
|
rl.EndTextureMode()
|
||||||
|
|
||||||
render_game_to_window(game)
|
render_game_to_window(game)
|
||||||
@@ -121,6 +161,7 @@ game_render :: proc(game: ^Game) {
|
|||||||
game_quit :: proc(game: ^Game) {
|
game_quit :: proc(game: ^Game) {
|
||||||
audio_manager_quit(&game.audio)
|
audio_manager_quit(&game.audio)
|
||||||
|
|
||||||
|
rl.UnloadShader(game.crt_shader)
|
||||||
rl.UnloadTexture(game.bg_texture)
|
rl.UnloadTexture(game.bg_texture)
|
||||||
rl.UnloadRenderTexture(game.viewport)
|
rl.UnloadRenderTexture(game.viewport)
|
||||||
delete(GLOBAL_EVENTS)
|
delete(GLOBAL_EVENTS)
|
||||||
@@ -133,6 +174,11 @@ game_should_run :: proc(game: Game) -> bool {
|
|||||||
return !game.should_close && !rl.WindowShouldClose()
|
return !game.should_close && !rl.WindowShouldClose()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
game_trigger_shake :: proc(game: ^Game, intensity: f32, duration: f32 = 0.15) {
|
||||||
|
game.shake_intensity = max(game.shake_intensity, intensity)
|
||||||
|
game.shake_timer = duration
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------
|
||||||
// Window Upscale Render
|
// Window Upscale Render
|
||||||
// ------------------------------------------------------------------------------
|
// ------------------------------------------------------------------------------
|
||||||
@@ -156,6 +202,7 @@ game_set_resolution :: proc(game: ^Game, res_index: int) {
|
|||||||
render_game_to_window :: proc(game: ^Game) {
|
render_game_to_window :: proc(game: ^Game) {
|
||||||
texture := game.viewport.texture
|
texture := game.viewport.texture
|
||||||
|
|
||||||
|
rl.BeginShaderMode(game.crt_shader)
|
||||||
rl.DrawTexturePro(
|
rl.DrawTexturePro(
|
||||||
texture,
|
texture,
|
||||||
// src: inverted y coordinate flips opengl texture buffer correctly
|
// src: inverted y coordinate flips opengl texture buffer correctly
|
||||||
@@ -166,5 +213,5 @@ render_game_to_window :: proc(game: ^Game) {
|
|||||||
0,
|
0,
|
||||||
rl.WHITE,
|
rl.WHITE,
|
||||||
)
|
)
|
||||||
|
rl.EndShaderMode()
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user