diff --git a/main.c b/main.c index 3de9ea8..3d84b80 100644 --- a/main.c +++ b/main.c @@ -2,22 +2,91 @@ #include "raymath.h" #include -#define VIRTUAL_WIDTH 800 -#define VIRTUAL_HEIGHT 600 +#define VIRTUAL_WIDTH 640 +#define VIRTUAL_HEIGHT 360 -#define FONT_SIZE 20 +#define FONT_SIZE 30 #define PADDLE_SPEED 4.0 #define PADDLE_WIDTH VIRTUAL_WIDTH / 8.0 #define PADDLE_HEIGHT VIRTUAL_HEIGHT / 30.0 +#define BALL_RADIUS VIRTUAL_WIDTH / 80.0 +#define BALL_BASE_SPEED 2.0f +#define BALL_MUL_SPEED 0.15f + +typedef struct Player { + Rectangle shape; + int lives; + int score; +} Player; + +typedef struct Ball { + Rectangle shape; + Vector2 velocity; + float velocityMultiplier; + bool firstBounce; + bool velocityApplied; +} Ball; + +void MovePaddle(Player *player) { + Vector2 mouseDelta = GetMouseDelta(); + player->shape.x = Clamp(player->shape.x + mouseDelta.x, 0, VIRTUAL_WIDTH - PADDLE_WIDTH); +} + +void MoveBall(Ball *ball, Player *player) { + bool isColliding = CheckCollisionRecs(player->shape, ball->shape); + + if (ball->shape.x - BALL_RADIUS < 0 || ball->shape.x + BALL_RADIUS > VIRTUAL_WIDTH) { + ball->velocity = Vector2Multiply(ball->velocity, (Vector2){-1.0f, 1.0f}); + ball->velocityApplied = false; + } else if (ball->shape.y - BALL_RADIUS < 0) { + ball->velocity = Vector2Multiply(ball->velocity, (Vector2){1.0f, -1.0f}); + ball->velocityApplied = false; + } else if (isColliding) { + ball->velocity = Vector2Multiply(ball->velocity, (Vector2){1.0f, -1.0f}); + + // Add speed multiplier every bounce to increase difficulty + if (!ball->velocityApplied) { + ball->velocityMultiplier = Clamp(ball->velocityMultiplier + BALL_MUL_SPEED, 1.0f, 4.0f); + // Safety so the ball doesn't get stuck inside the paddle from sides + ball->shape.y = player->shape.y - BALL_RADIUS; + ball->velocityApplied = true; + } + + // When first bouncing off the paddle increase the speed to normal + if (!ball->firstBounce) { + ball->firstBounce = true; + ball->velocityMultiplier = 1.0f; + } + } else if (ball->shape.y + BALL_RADIUS > VIRTUAL_HEIGHT - PADDLE_HEIGHT + (BALL_RADIUS / 2.0f)) { + // Death or Game Over + if (player->lives > 0) { + player->lives -= 1; + ball->velocityMultiplier = 1.0f; + // Resetting ball's position + ball->shape.x = VIRTUAL_WIDTH / 2.0f; + ball->shape.y = VIRTUAL_HEIGHT / 2.0f; + } else { + } + ball->velocityApplied = false; + } + + if (!ball->firstBounce) { + ball->velocityMultiplier = 0.5f; + } + + ball->shape.x += ball->velocity.x * ball->velocityMultiplier; + ball->shape.y += ball->velocity.y * ball->velocityMultiplier; +} + int main() { InitWindow(0, 0, "Breakout"); ToggleFullscreen(); - const int currentMonitor = GetCurrentMonitor(); - const int monitorWidth = GetMonitorWidth(currentMonitor); - const int monitorHeight = GetMonitorHeight(currentMonitor); + const int CURRENT_MONITOR = GetCurrentMonitor(); + const int MONITOR_WIDTH = GetMonitorWidth(CURRENT_MONITOR); + const int MONITOR_HEIGHT = GetMonitorHeight(CURRENT_MONITOR); SetTargetFPS(60); @@ -25,26 +94,38 @@ int main() { // camera.offset = (Vector2){SCREEN_WIDTH / 2.0f, SCREEN_HEIGHT / 2.0f}; camera.zoom = 1.0f; - const RenderTexture2D renderTexture = LoadRenderTexture(VIRTUAL_WIDTH, VIRTUAL_HEIGHT); - const Rectangle renderTextureSrc = (Rectangle){0.0f, 0.0f, VIRTUAL_WIDTH, -VIRTUAL_HEIGHT}; - const Rectangle renderTextureDest = (Rectangle){0.0f, 0.0f, monitorWidth, monitorHeight}; - const Vector2 renderTextureOrig = {0}; + const RenderTexture2D RENDER_TEXTURE = LoadRenderTexture(VIRTUAL_WIDTH, VIRTUAL_HEIGHT); + const Rectangle RENDER_TEXTURE_SRC = (Rectangle){0.0f, 0.0f, VIRTUAL_WIDTH, -VIRTUAL_HEIGHT}; + const Rectangle RENDER_TEXTURE_DST = (Rectangle){0.0f, 0.0f, MONITOR_WIDTH, MONITOR_HEIGHT}; + const Vector2 RENDER_TEXTURE_POS = {0}; - float paddleX = 0; - Vector2 ballPos = (Vector2){VIRTUAL_WIDTH / 2.0f, VIRTUAL_HEIGHT / 2.0f}; - int score = 0; + Ball ball = { + (Rectangle){VIRTUAL_WIDTH / 2.0f, VIRTUAL_HEIGHT / 2.0f, BALL_RADIUS, BALL_RADIUS}, + (Vector2){BALL_BASE_SPEED, BALL_BASE_SPEED}, + 1.0f, + false, + false, + }; + + Player player = { + (Rectangle){VIRTUAL_WIDTH / 2.0f, VIRTUAL_HEIGHT - PADDLE_HEIGHT - 10, PADDLE_WIDTH, PADDLE_HEIGHT}, 3, 0}; while (!WindowShouldClose()) { - Vector2 mouseDelta = GetMouseDelta(); + // Keep before the cursor is disabled otherwise the paddle is stuck + MovePaddle(&player); DisableCursor(); - paddleX = Clamp(paddleX + mouseDelta.x, 0, VIRTUAL_WIDTH - PADDLE_WIDTH); + MoveBall(&ball, &player); + // TraceLog(LOG_INFO, "%.0fx%.0f", ball.shape.x, ball.shape.y); - BeginTextureMode(renderTexture); + BeginTextureMode(RENDER_TEXTURE); { ClearBackground(BLACK); - DrawRectangle(paddleX, VIRTUAL_HEIGHT - PADDLE_HEIGHT - 10, PADDLE_WIDTH, PADDLE_HEIGHT, RAYWHITE); + + DrawRectangleRec(ball.shape, RAYWHITE); + + DrawRectangleRec(player.shape, RAYWHITE); } EndTextureMode(); @@ -53,19 +134,21 @@ int main() { ClearBackground(BLACK); BeginMode2D(camera); { - DrawCircle(ballPos.x, ballPos.y, 20, RAYWHITE); - - DrawTexturePro(renderTexture.texture, renderTextureSrc, renderTextureDest, renderTextureOrig, 0, WHITE); + DrawTexturePro(RENDER_TEXTURE.texture, RENDER_TEXTURE_SRC, RENDER_TEXTURE_DST, RENDER_TEXTURE_POS, 0, + RAYWHITE); } EndMode2D(); - // char score[] = "Score"; - const char *scoreText = TextFormat("Score %08i", score); - float scoreWidth = MeasureText(scoreText, FONT_SIZE) + 10; + const char *scoreText = TextFormat("Score %08i", player.score); + // float scoreWidth = MeasureText(scoreText, FONT_SIZE) + 10; DrawText(scoreText, 10, 10, FONT_SIZE, RAYWHITE); + const char *livesText = TextFormat("Lives %02i", player.lives); + DrawText(livesText, 10, 50, FONT_SIZE, RAYWHITE); + DrawFPS(GetScreenWidth() - 80, 5); // DrawText(TextFormat("%ix%i", monitorWidth, monitorHeight), 10, 40, FONT_SIZE, GOLD); + DrawText(TextFormat("Speed %.02f", ball.velocityMultiplier), 10, 90, FONT_SIZE, GOLD); } EndDrawing(); }