diff --git a/main.c b/main.c index 233aaa7..bff5ccb 100644 --- a/main.c +++ b/main.c @@ -14,8 +14,14 @@ #define BALL_RADIUS (VIRTUAL_WIDTH / 80.0) #define BALL_BASE_SPEED 2.0f +#define BALL_VELOCITY ((Vector2){BALL_BASE_SPEED, BALL_BASE_SPEED}) #define BALL_MUL_SPEED 0.15f +#define BLOCK_ROWS 7 +#define BLOCKS_PER_ROW 10 +#define BLOCK_WIDTH (int)rint((float)VIRTUAL_WIDTH / (float)BLOCKS_PER_ROW) - 1 +#define BLOCK_HEIGHT 25 + #define LEN(arr) (sizeof arr / sizeof *arr) typedef struct Player { @@ -38,21 +44,41 @@ typedef struct Block { bool isDestroyed; } Block; -void MovePaddle(Player *player) { +void BlocksReset(Block blocks[BLOCK_ROWS * BLOCKS_PER_ROW]) { + for (int i = 0; i < BLOCK_ROWS; i++) { + for (int j = 0; j < BLOCKS_PER_ROW; j++) { + int id = i + (j * BLOCK_ROWS); + int blockX = j * (BLOCK_WIDTH + 1); + int blockY = 10 + i * (BLOCK_HEIGHT + 1); + + int hue = (id % BLOCK_ROWS) * 400; + Color color = ColorFromHSV(hue, 1.0f, 1.0f); + + blocks[id] = (Block){(Rectangle){blockX, blockY, BLOCK_WIDTH, BLOCK_HEIGHT}, color, false}; + } + } +} + +void PaddleMove(Player *player) { Vector2 mouseDelta = GetMouseDelta(); player->shape.x = Clamp(player->shape.x + mouseDelta.x, 0, VIRTUAL_WIDTH - PADDLE_WIDTH); } -void ResetBall(Ball *ball) { +void BallReset(Ball *ball) { ball->velocityMultiplier = 1.0f; - // Resetting ball's position - ball->velocity = (Vector2){BALL_BASE_SPEED, BALL_BASE_SPEED}; + ball->velocity = BALL_VELOCITY; ball->firstBounce = false; ball->shape.x = VIRTUAL_WIDTH / 2.0f; - ball->shape.y = VIRTUAL_HEIGHT / 2.0f; + ball->shape.y = (VIRTUAL_HEIGHT / 2.0f) + 20.0; } -void MoveBall(Ball *ball, Player *player) { +void BallChangeVelocity(Ball *ball, Vector2 force) { + ball->velocity = Vector2Multiply(ball->velocity, force); + ball->velocity = Vector2Normalize(ball->velocity); + ball->velocity = Vector2Multiply(ball->velocity, BALL_VELOCITY); +} + +void BallMove(Ball *ball, Player *player) { bool isColliding = CheckCollisionRecs(player->shape, ball->shape); if (ball->shape.x < 0 || ball->shape.x + BALL_RADIUS > VIRTUAL_WIDTH) { @@ -62,11 +88,15 @@ void MoveBall(Ball *ball, Player *player) { ball->velocity = Vector2Multiply(ball->velocity, (Vector2){1.0f, -1.0f}); ball->velocityApplied = false; } else if (isColliding) { - float direction = 1 - (ball->shape.x + (BALL_RADIUS / 2.0f)) / (player->shape.x + (PADDLE_WIDTH / 2.0f) + 1); - float forceX = 1.0f; // direction > 0 ? 1.0f : -1.0f; - float forceY = -1.0f; // + (direction * 10.0); - TraceLog(LOG_INFO, "%f %f %f", direction, forceX, forceY); - ball->velocity = Vector2Multiply(ball->velocity, (Vector2){forceX, forceY}); + float paddleCenter = player->shape.x + (PADDLE_WIDTH / 2.0f); + float ballCenter = ball->shape.x + (BALL_RADIUS / 2.0f); + + float direction = ball->velocity.x / BALL_BASE_SPEED; + float boost = (ballCenter / paddleCenter) - 1.0f; + float forceX = (boost * direction) > 0 ? 1.0f : -1.0f; + float forceY = -(1.5f - (fabs(boost) * 10.0)); + forceY = Clamp(forceY, -1.4f, -0.8f); + BallChangeVelocity(ball, (Vector2){forceX, forceY}); // Add speed multiplier every bounce to increase difficulty if (!ball->velocityApplied) { @@ -79,13 +109,13 @@ void MoveBall(Ball *ball, Player *player) { // When first bouncing off the paddle increase the speed to normal if (!ball->firstBounce) { ball->firstBounce = true; - ball->velocityMultiplier = 1.0f; + ball->velocityMultiplier = 1.5f; } } 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; - ResetBall(ball); + BallReset(ball); } else { } ball->velocityApplied = false; @@ -100,6 +130,7 @@ void MoveBall(Ball *ball, Player *player) { } int main() { + SetConfigFlags(FLAG_VSYNC_HINT); InitWindow(0, 0, "Breakout"); ToggleFullscreen(); @@ -107,7 +138,8 @@ int main() { const int MONITOR_WIDTH = GetMonitorWidth(CURRENT_MONITOR); const int MONITOR_HEIGHT = GetMonitorHeight(CURRENT_MONITOR); - SetTargetFPS(60); + int refreshRate = GetMonitorRefreshRate(CURRENT_MONITOR); + SetTargetFPS(refreshRate); Camera2D camera = {0}; // Game world camera camera.zoom = 1.0f; @@ -119,48 +151,35 @@ int main() { Ball ball = { (Rectangle){VIRTUAL_WIDTH / 2.0f, VIRTUAL_HEIGHT / 2.0f, BALL_RADIUS, BALL_RADIUS}, - (Vector2){BALL_BASE_SPEED, BALL_BASE_SPEED}, + BALL_VELOCITY, 1.0f, false, false, }; + BallReset(&ball); Player player = { (Rectangle){VIRTUAL_WIDTH / 2.0f, VIRTUAL_HEIGHT - PADDLE_HEIGHT - 10, PADDLE_WIDTH, PADDLE_HEIGHT}, PLAYER_LIVES, 0}; - const int BLOCK_ROWS = 7; - const int BLOCKS_PER_ROW = 15; - const int BLOCK_WIDTH = VIRTUAL_WIDTH / (float)BLOCKS_PER_ROW; - const int BLOCK_HEIGHT = 20; Block blocks[BLOCK_ROWS * BLOCKS_PER_ROW]; - - for (int i = 0; i < BLOCK_ROWS; i++) { - for (int j = 0; j < BLOCKS_PER_ROW; j++) { - int id = i + (j * BLOCK_ROWS); - int blockX = j * (BLOCK_WIDTH + 1); - int blockY = 10 + i * (BLOCK_HEIGHT + 1); - - int hue = (id % BLOCK_ROWS) * 400; - Color color = ColorFromHSV(hue, 1.0f, 1.0f); - - blocks[id] = (Block){(Rectangle){blockX, blockY, BLOCK_WIDTH, BLOCK_HEIGHT}, color, false}; - } - } + BlocksReset(blocks); const int BLOCK_COUNT = LEN(blocks); + int brokenBlocks = 0; while (!WindowShouldClose()) { if (IsKeyDown(KEY_R)) { - ResetBall(&ball); + BallReset(&ball); + BlocksReset(blocks); } // Keep before the cursor is disabled otherwise the paddle is stuck - MovePaddle(&player); + PaddleMove(&player); DisableCursor(); - MoveBall(&ball, &player); + BallMove(&ball, &player); BeginTextureMode(RENDER_TEXTURE); { @@ -180,10 +199,37 @@ int main() { bool isColliding = CheckCollisionRecs(ball.shape, block->shape); if (isColliding) { - ball.velocity = Vector2Multiply(ball.velocity, (Vector2){1.0f, -1.0f}); + float blockCenterX = block->shape.x + (BLOCK_WIDTH / 2.0f); + float blockCenterY = block->shape.y + (BLOCK_HEIGHT / 2.0f); + + float ballCenterX = ball.shape.x + (BALL_RADIUS / 2.0f); + float ballCenterY = ball.shape.y + (BALL_RADIUS / 2.0f); + + Vector2 dir = {ballCenterX - blockCenterX, ballCenterY - blockCenterY}; + dir = Vector2Normalize(dir); + + Vector2 force = {1.0f, -1.0f}; + if (dir.y < 0 && dir.y > dir.x) { + force.y = -1.0f; + } else if (dir.x > 0 && dir.x > dir.y) { + force.x = -1.0f; + } + + ball.velocity = Vector2Multiply(ball.velocity, force); + ball.velocityApplied = false; block->color = BLACK; block->isDestroyed = true; player.score += 100; + brokenBlocks += 1; + + if (brokenBlocks >= BLOCK_COUNT) { + BallReset(&ball); + BlocksReset(blocks); + player.score += 1000; + brokenBlocks = 0; + } + + continue; } DrawRectangleRec(block->shape, block->color);