Created
June 22, 2025 01:21
-
-
Save naranyala/c40117f3afd6dc6b1b6cf540ea2a6958 to your computer and use it in GitHub Desktop.
classic breakout game with odin+raylib
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import "core:math/rand" | |
import "core:fmt" | |
import rl "vendor:raylib" | |
Ball :: struct { | |
pos: rl.Vector2, | |
velocity: rl.Vector2, | |
radius: f32, | |
} | |
Block :: struct { | |
rect: rl.Rectangle, | |
color: rl.Color, | |
active: bool, | |
} | |
Paddle :: struct { | |
rect: rl.Rectangle, | |
speed: f32, | |
} | |
main :: proc() { | |
SCREEN_WIDTH :: 800 | |
SCREEN_HEIGHT :: 600 | |
rl.InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "Breakout Game") | |
defer rl.CloseWindow() | |
rl.SetTargetFPS(60) | |
// Initialize ball | |
ball := Ball{ | |
pos = rl.Vector2{SCREEN_WIDTH / 2, SCREEN_HEIGHT - 100}, | |
velocity = rl.Vector2{250, -250}, | |
radius = 10, | |
} | |
// Initialize paddle | |
paddle := Paddle{ | |
rect = rl.Rectangle{ | |
x = SCREEN_WIDTH / 2 - 60, | |
y = SCREEN_HEIGHT - 40, | |
width = 120, | |
height = 15, | |
}, | |
speed = 400, | |
} | |
// Initialize blocks | |
blocks := make([dynamic]Block) | |
defer delete(blocks) | |
// Create grid of blocks | |
block_width: f32 = 70 | |
block_height: f32 = 25 | |
for row in 0..<6 { | |
for col in 0..<10 { | |
block := Block{ | |
rect = rl.Rectangle{ | |
x = f32(col) * (block_width + 5) + 25, | |
y = f32(row) * (block_height + 3) + 70, | |
width = block_width, | |
height = block_height, | |
}, | |
color = rl.Color{ | |
u8(255 - i32(row) * 25), | |
u8(50 + i32(row) * 25), | |
u8(100 + i32(col) * 10), | |
255, | |
}, | |
active = true, | |
} | |
append(&blocks, block) | |
} | |
} | |
game_over := false | |
ball_lost := false | |
for !rl.WindowShouldClose() { | |
dt := rl.GetFrameTime() | |
// Reset game on R press | |
if rl.IsKeyPressed(rl.KeyboardKey.R) { | |
ball.pos = rl.Vector2{SCREEN_WIDTH / 2, SCREEN_HEIGHT - 100} | |
ball.velocity = rl.Vector2{250, -250} | |
paddle.rect.x = SCREEN_WIDTH / 2 - 60 | |
game_over = false | |
ball_lost = false | |
for &block in blocks { | |
block.active = true | |
} | |
} | |
if !game_over && !ball_lost { | |
// Move paddle | |
if rl.IsKeyDown(rl.KeyboardKey.LEFT) || rl.IsKeyDown(rl.KeyboardKey.A) { | |
paddle.rect.x -= paddle.speed * dt | |
if paddle.rect.x < 0 do paddle.rect.x = 0 | |
} | |
if rl.IsKeyDown(rl.KeyboardKey.RIGHT) || rl.IsKeyDown(rl.KeyboardKey.D) { | |
paddle.rect.x += paddle.speed * dt | |
if paddle.rect.x + paddle.rect.width > SCREEN_WIDTH { | |
paddle.rect.x = SCREEN_WIDTH - paddle.rect.width | |
} | |
} | |
// Move ball | |
ball.pos.x += ball.velocity.x * dt | |
ball.pos.y += ball.velocity.y * dt | |
// Bounce off side walls | |
if ball.pos.x - ball.radius <= 0 || ball.pos.x + ball.radius >= SCREEN_WIDTH { | |
ball.velocity.x = -ball.velocity.x | |
ball.pos.x = ball.radius if ball.pos.x < ball.radius else SCREEN_WIDTH - ball.radius | |
} | |
// Bounce off top wall | |
if ball.pos.y - ball.radius <= 0 { | |
ball.velocity.y = -ball.velocity.y | |
ball.pos.y = ball.radius | |
} | |
// Check if ball fell off bottom | |
if ball.pos.y > SCREEN_HEIGHT + 50 { | |
ball_lost = true | |
} | |
// Check collision with paddle | |
if rl.CheckCollisionCircleRec(ball.pos, ball.radius, paddle.rect) && ball.velocity.y > 0 { | |
ball.velocity.y = -ball.velocity.y | |
// Add spin based on where ball hits paddle | |
paddle_center := paddle.rect.x + paddle.rect.width / 2 | |
hit_pos := (ball.pos.x - paddle_center) / (paddle.rect.width / 2) | |
ball.velocity.x += hit_pos * 200 // Add horizontal velocity based on hit position | |
// Ensure ball doesn't get stuck in paddle | |
ball.pos.y = paddle.rect.y - ball.radius - 1 | |
} | |
// Check collision with blocks | |
for &block in blocks { | |
if !block.active do continue | |
if rl.CheckCollisionCircleRec(ball.pos, ball.radius, block.rect) { | |
block.active = false | |
// Calculate bounce direction | |
center_x := block.rect.x + block.rect.width / 2 | |
center_y := block.rect.y + block.rect.height / 2 | |
dx := ball.pos.x - center_x | |
dy := ball.pos.y - center_y | |
if abs(dx / block.rect.width) > abs(dy / block.rect.height) { | |
// Hit from left or right | |
ball.velocity.x = -ball.velocity.x | |
} else { | |
// Hit from top or bottom | |
ball.velocity.y = -ball.velocity.y | |
} | |
break // Only hit one block per frame | |
} | |
} | |
// Check win condition | |
active_blocks := 0 | |
for block in blocks { | |
if block.active do active_blocks += 1 | |
} | |
if active_blocks == 0 { | |
game_over = true | |
} | |
} | |
rl.BeginDrawing() | |
defer rl.EndDrawing() | |
rl.ClearBackground(rl.Color{15, 15, 25, 255}) | |
// Draw blocks | |
for block in blocks { | |
if block.active { | |
rl.DrawRectangleRec(block.rect, block.color) | |
rl.DrawRectangleLinesEx(block.rect, 1, rl.WHITE) | |
} | |
} | |
// Draw paddle | |
rl.DrawRectangleRec(paddle.rect, rl.BLUE) | |
rl.DrawRectangleLinesEx(paddle.rect, 2, rl.LIGHTGRAY) | |
// Draw ball | |
if !ball_lost { | |
rl.DrawCircleV(ball.pos, ball.radius, rl.WHITE) | |
rl.DrawCircleLinesV(ball.pos, ball.radius, rl.GRAY) | |
} | |
// Draw single line UI at top | |
// Count remaining blocks first | |
active_blocks := 0 | |
for block in blocks { | |
if block.active do active_blocks += 1 | |
} | |
ui_text := fmt.ctprintf("LEFT/RIGHT: Move Paddle | R: Reset | Blocks: %d", active_blocks) | |
rl.DrawText(ui_text, 10, 10, 16, rl.WHITE) | |
// Game state messages | |
if game_over { | |
rl.DrawText("YOU WIN!", SCREEN_WIDTH/2 - 100, SCREEN_HEIGHT/2, 40, rl.GREEN) | |
rl.DrawText("Press R to play again", SCREEN_WIDTH/2 - 120, SCREEN_HEIGHT/2 + 50, 20, rl.WHITE) | |
} else if ball_lost { | |
rl.DrawText("BALL LOST!", SCREEN_WIDTH/2 - 100, SCREEN_HEIGHT/2, 40, rl.RED) | |
rl.DrawText("Press R to try again", SCREEN_WIDTH/2 - 120, SCREEN_HEIGHT/2 + 50, 20, rl.WHITE) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment