Pong in C with Raylib
The sheep have finished lambing and I finally have some spare time. I've been AFK for a few weeks and during that time I've been reflecting on life as a programmer. Upon this reflection I have made a decision. I want to write C. Writing C feels like proper programming and I wish to be a proper programmer.
This brings us to today's post. In order to increase my efficiency with C I will be undertaking various programming projects. To ease myself in, we have today's project "write a pong clone".
The Clone

I chose a Pong clone to start as it's super simple. There are only 3 objects, the two paddles and the ball, so everything can be kept on the stack. No need for memory allocation here, and I'm not going to bother with assets like sound or sprites, it's not needed for this one.
The following Pong clone was written in about 2 hours. I didn't refactor anything as I went along. I felt it unnessary, it's not like any one will actually play or use this. It's simply an exercise.
Check out the code below and critique the workings of my naive programming brain.
The next challenge is an Asteroids clone, which is a slight step up. I'll try to make a proper game loop for that one.
The Code
#include "raylib.h"
#include <stdio.h>
#include <stdlib.h>
struct Court
{
float x;
float y;
int w;
int h;
};
struct Ball
{
float x;
float y;
int vx;
int vy;
int speed;
int r;
};
void drawBall(struct Ball *b)
{
DrawCircle(b->x, b->y, b->r, RAYWHITE);
}
struct Paddle
{
float x;
float y;
float vx;
float vy;
int w;
int h;
};
void drawPaddle(struct Paddle *p)
{
DrawRectangle(p->x, p->y, p->w, p->h, RAYWHITE);
}
struct Game
{
int playerScore;
int computerScore;
};
int main(void)
{
struct Court court = { 100, 100, 400, 600 };
Vector2 courtTopLeft = {court.x, court.y};
Vector2 courtTopRight = {court.x + court.w, court.y};
Vector2 courtBotLeft = {court.x, court.y + court.h};
Vector2 courtBotRight = {court.x + court.w, court.y + court.h};
float courtLineThickness = 4;
InitWindow(600, 800, "Pong");
SetRandomSeed(1337);
struct Game game = { 0, 0 };
struct Ball ball;
struct Paddle topPaddle;
struct Paddle bottomPaddle;
ball.r = 10;
ball.x = court.x + (court.w / 2);
ball.y = court.y + (court.h / 2);
ball.vx = GetRandomValue(0, 1) ? 1 : -1;
ball.vy = -1;
ball.speed=100;
int paddleYBuffer = 20;
topPaddle.y = court.y + paddleYBuffer;
bottomPaddle.y = court.y + court.h - (paddleYBuffer * 2);
int initialPaddleWidth = 80;
int initialPaddleHeight = 20;
topPaddle.w = initialPaddleWidth;
topPaddle.h = initialPaddleHeight;
bottomPaddle.w = initialPaddleWidth;
bottomPaddle.h = initialPaddleHeight;
topPaddle.x = court.x + (court.w / 2) - (topPaddle.w / 2);
bottomPaddle.x = court.x + (court.w / 2) - (bottomPaddle.w / 2);
Vector2 playerMovement;
int paddleMoveSpeed = 400;
Vector2 playerVelocity;
float friction = 0.78;
int cellSize = 100;
int rowCount = GetScreenHeight() / cellSize;
int colCount = GetScreenWidth() / cellSize;
float bottomHit = 0.0;
float topHit = 0.0;
while (!WindowShouldClose())
{
//update
if(IsKeyDown(KEY_R))
{
game.playerScore = 0;
game.computerScore = 0;
}
if(IsKeyDown(KEY_LEFT))
{
playerVelocity.x = -1;
}
if(IsKeyDown(KEY_RIGHT))
{
playerVelocity.x = 1;
}
if(topPaddle.vx)
{
topPaddle.x += topPaddle.vx * (paddleMoveSpeed * GetFrameTime());
topPaddle.vx *= friction;
if(topPaddle.vx < 0.1 && topPaddle.vx > -0.1)
{
topPaddle.vx=0;
}
}
if(topPaddle.x < court.x)
{
topPaddle.x = court.x;
}
if(topPaddle.x + topPaddle.w > court.x + court.w)
{
topPaddle.x = court.x + court.w - topPaddle.w;
}
if(playerVelocity.x)
{
bottomPaddle.x += playerVelocity.x * (paddleMoveSpeed * GetFrameTime());
playerVelocity.x *= friction;
if(playerVelocity.x < 0.1 && playerVelocity.x > -0.1)
{
playerVelocity.x=0;
}
}
if(bottomPaddle.x < court.x)
{
bottomPaddle.x = court.x;
}
if(bottomPaddle.x + bottomPaddle.w > court.x + court.w)
{
bottomPaddle.x = court.x + court.w - bottomPaddle.w;
}
ball.x += ball.vx * (paddleMoveSpeed * GetFrameTime());
ball.y += ball.vy * (paddleMoveSpeed * GetFrameTime());
if(ball.x - ball.r < court.x)
{
ball.vx = 1;
}
if(ball.x + ball.r > court.x + court.w)
{
ball.vx = -1;
}
if(ball.y - ball.r < court.y)
{
ball.vy = 1;
}
if(ball.y + ball.r > court.y + court.h)
{
ball.vy = -1;
}
Vector2 center = { ball.x, ball.y };
Rectangle botRect = { bottomPaddle.x, bottomPaddle.y, bottomPaddle.w, bottomPaddle.h };
if(CheckCollisionCircleRec(center, ball.r, botRect))
{
ball.vy = -1;
ball.vx = GetRandomValue(0, 1) ? -1 : 1;
}
Rectangle topRect = { topPaddle.x, topPaddle.y, topPaddle.w, topPaddle.h };
if(CheckCollisionCircleRec(center, ball.r, topRect))
{
ball.vy = 1;
ball.vx = GetRandomValue(0, 1) ? -1 : 1;
}
if(ball.vy == -1)
{
if(topPaddle.x > ball.x + ball.r)
{
topPaddle.vx = -1;
}
if(topPaddle.x +topPaddle.w < ball.x - ball.r)
{
topPaddle.vx = 1;
}
}
if(ball.vy == 1)
{
if(topPaddle.x + ((float)topPaddle.w / 2) > court.x + court.w /2)
{
topPaddle.vx = -1;
}
if(topPaddle.x + ((float)topPaddle.w / 2) < court.x + court.w / 2)
{
topPaddle.vx = 1;
}
}
if(topHit)
{
topHit -= 0.01;
}
if(bottomHit)
{
bottomHit -= 0.01;
}
if(CheckCollisionCircleLine(center, ball.r, courtTopLeft, courtTopRight))
{
topHit = 1.0;
game.playerScore += 1;
}
if(CheckCollisionCircleLine(center, ball.r, courtBotLeft, courtBotRight))
{
bottomHit = 1.0;
game.computerScore += 1;
}
//draw
BeginDrawing();
ClearBackground(BLACK);
for(int rowIndex = 0; rowIndex < rowCount; rowIndex++)
{
for(int colIndex = 0; colIndex < colCount; colIndex++)
{
DrawRectangle(
colIndex * cellSize,
rowIndex * cellSize,
cellSize,
cellSize,
(colIndex + rowIndex) % 2 == 0 ? GREEN : LIME
);
}
}
char scoreText[32];
snprintf(scoreText, sizeof(scoreText), "%d : %d", game.playerScore, game.computerScore);
int fontSize = 80;
int textWidth = MeasureText(scoreText, fontSize);
DrawText(scoreText, court.x - textWidth / 2 + court.w / 2, 10, fontSize, RAYWHITE);
//top
DrawLineEx(courtTopLeft, courtTopRight, courtLineThickness, topHit > 0 ? RED : RAYWHITE);
//bot
DrawLineEx(courtBotLeft, courtBotRight, courtLineThickness, bottomHit > 0 ? RED : RAYWHITE);
//left
DrawLineEx(courtTopLeft, courtBotLeft, courtLineThickness, RAYWHITE);
//right
DrawLineEx(courtTopRight, courtBotRight, courtLineThickness, RAYWHITE);
drawBall(&ball);
drawPaddle(&topPaddle);
drawPaddle(&bottomPaddle);
EndDrawing();
}
CloseWindow();
return 0;
}