From 40ef2b8237b03413955a00e9abb812e00e63eea1 Mon Sep 17 00:00:00 2001 From: Ninad Sachania Date: Sun, 14 Dec 2025 00:44:20 +0530 Subject: [PATCH] Port Arkanoid from raylib-games --- .github/workflows/check.yml | 1 + raylib/arkanoid/arkanoid.odin | 254 ++++++++++++++++++++++++++++++++++ 2 files changed, 255 insertions(+) create mode 100644 raylib/arkanoid/arkanoid.odin diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index a11d7ca..43fbb3b 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -88,6 +88,7 @@ jobs: odin check raylib/ports/textures/textures_background_scrolling.odin -file $FLAGS odin check raylib/ports/textures/textures_blend_modes.odin -file $FLAGS odin check raylib/tetroid $FLAGS + odin check raylib/arkanoid $FLAGS odin check raylib/box2d $FLAGS odin check raylib/ports/core/core_3d_camera_fps.odin -file $FLAGS odin check raylib/ports/core/core_basic_window.odin -file $FLAGS diff --git a/raylib/arkanoid/arkanoid.odin b/raylib/arkanoid/arkanoid.odin new file mode 100644 index 0000000..7872498 --- /dev/null +++ b/raylib/arkanoid/arkanoid.odin @@ -0,0 +1,254 @@ +package main + +// /******************************************************************************************* +// * +// * raylib - classic game: arkanoid +// * +// * Sample game developed by Marc Palau and Ramon Santamaria +// * +// * This game has been created using raylib v1.3 (www.raylib.com) +// * raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details) +// * +// * Copyright (c) 2015 Ramon Santamaria (@raysan5) +// * +// ********************************************************************************************/ + + +import rl "vendor:raylib" + +PLAYER_MAX_LIFE :: 5 +LINES_OF_BRICKS :: 5 +BRICKS_PER_LINE :: 20 + +Player :: struct { + position: rl.Vector2, + size: rl.Vector2, + life: int, +} + +Ball :: struct { + position: rl.Vector2, + speed: rl.Vector2, + radius: f32, + active: bool, +} + +Brick :: struct { + position: rl.Vector2, + active: bool, +} + +SCREEN_WIDTH :: 800 +SCREEN_HEIGHT :: 450 + +game_over := false +pause := false + +player: Player +ball: Ball +brick: [LINES_OF_BRICKS][BRICKS_PER_LINE]Brick +brick_size: rl.Vector2 + +main :: proc() { + // Initialization (Note windowTitle is unused on Android) + rl.InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "classic game: arkanoid") + + init_game() + + rl.SetTargetFPS(60) + + // Main game loop + for !rl.WindowShouldClose() { // Detect window close button or ESC key + update_draw_frame() + } + + unload_game() // Unload loaded data (textures, sounds, models...) + + rl.CloseWindow() // Close window and OpenGL context + +} + +// Initialize game variables +init_game :: proc() { + brick_size = { f32(rl.GetScreenWidth()/BRICKS_PER_LINE), 40 } + + // Initialize player + player.position = { SCREEN_WIDTH/2, SCREEN_HEIGHT*7/8 } + player.size = { SCREEN_WIDTH/10, 20 } + player.life = PLAYER_MAX_LIFE + + // Initialize ball + ball.position = { player.position.x, player.position.y - player.size.y/2 - ball.radius } + ball.speed = { 0, 0 } + ball.radius = 7 + ball.active = false + + // Initialize bricks + initialDownPosition := 50 + + for i in 0..= SCREEN_WIDTH { player.position.x = SCREEN_WIDTH - player.size.x/2 } + + // Ball launching logic + if !ball.active { + if rl.IsKeyPressed(.SPACE) { + ball.active = true + ball.speed = { 0, -5 } + } + } + + // Ball movement logic + if ball.active { + ball.position.x += ball.speed.x + ball.position.y += ball.speed.y + } else { + ball.position = { player.position.x, player.position.y - player.size.y/2 - ball.radius } + } + + // Collision logic: ball vs walls + if ((ball.position.x + ball.radius) >= SCREEN_WIDTH) || ((ball.position.x - ball.radius) <= 0) { ball.speed.x *= -1 } + if (ball.position.y - ball.radius) <= 0 { ball.speed.y *= -1 } + if (ball.position.y + ball.radius) >= SCREEN_HEIGHT { + ball.speed = { 0, 0 } + ball.active = false + + player.life -= 1 + } + + // Collision logic: ball vs player + if rl.CheckCollisionCircleRec(ball.position, ball.radius, + (rl.Rectangle){ player.position.x - player.size.x/2, player.position.y - player.size.y/2, player.size.x, player.size.y}) { + if ball.speed.y > 0 { + ball.speed.y *= -1 + ball.speed.x = (ball.position.x - player.position.x)/(player.size.x/2)*5 + } + } + + // Collision logic: ball vs bricks + for i in 0.. (brick[i][j].position.y + brick_size.y/2 + ball.speed.y)) && + ((abs(ball.position.x - brick[i][j].position.x)) < (brick_size.x/2 + ball.radius*2/3)) && (ball.speed.y < 0) { + + // Hit below + brick[i][j].active = false + ball.speed.y *= -1 + } else if ((ball.position.y + ball.radius) >= (brick[i][j].position.y - brick_size.y/2)) && + ((ball.position.y + ball.radius) < (brick[i][j].position.y - brick_size.y/2 + ball.speed.y)) && + ((abs(ball.position.x - brick[i][j].position.x)) < (brick_size.x/2 + ball.radius*2/3)) && (ball.speed.y > 0) { + // Hit above + brick[i][j].active = false + ball.speed.y *= -1 + } else if ((ball.position.x + ball.radius) >= (brick[i][j].position.x - brick_size.x/2)) && + ((ball.position.x + ball.radius) < (brick[i][j].position.x - brick_size.x/2 + ball.speed.x)) && + ((abs(ball.position.y - brick[i][j].position.y)) < (brick_size.y/2 + ball.radius*2/3)) && (ball.speed.x > 0) { + // Hit left + brick[i][j].active = false + ball.speed.x *= -1 + } else if ((ball.position.x - ball.radius) <= (brick[i][j].position.x + brick_size.x/2)) && + ((ball.position.x - ball.radius) > (brick[i][j].position.x + brick_size.x/2 + ball.speed.x)) && + ((abs(ball.position.y - brick[i][j].position.y)) < (brick_size.y/2 + ball.radius*2/3)) && (ball.speed.x < 0) { + // Hit right + brick[i][j].active = false + ball.speed.x *= -1 + } + } + } + } + + // Game over logic + if player.life <= 0 { + game_over = true + } else { + game_over = true + + for i in 0..