initial commit
This commit is contained in:
commit
8c61e53dcd
13
.clang-format
Normal file
13
.clang-format
Normal file
@ -0,0 +1,13 @@
|
||||
BasedOnStyle: Microsoft
|
||||
|
||||
# Specify column limit of 80, this way you can squize more terminals on screen
|
||||
ColumnLimit: 80
|
||||
|
||||
# Pointers are part of the type not the variable name
|
||||
PointerAlignment: Left
|
||||
|
||||
# Consecutive macros are more readable
|
||||
AlignConsecutiveMacros: true
|
||||
|
||||
# It is more readable to have short {} statements on the same line
|
||||
AllowShortBlocksOnASingleLine: true
|
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
# Do not include compiled object files into git
|
||||
*.o
|
||||
# Do not include the built binary into git
|
||||
pong
|
45
Makefile
Normal file
45
Makefile
Normal file
@ -0,0 +1,45 @@
|
||||
CC=gcc
|
||||
LD=gcc
|
||||
|
||||
# I'm using GCC to link code. This is prefered over using LD directly since
|
||||
# it will handle all the GLIBC linkage for you.
|
||||
|
||||
SRCS = $(wildcard src/*.c)
|
||||
OBJS = ${SRCS:.c=.o}
|
||||
|
||||
DEST = pong
|
||||
|
||||
CC_FLAGS = -I include/ -g -Wall -Wextra -Werror -Wno-unused-parameter -Wno-unused-variable -Wno-unused-function -O3
|
||||
LD_FLAGS =
|
||||
LIBS = -lSDL2
|
||||
|
||||
# This is to let make know these are not actually file targets but rather
|
||||
# command targets.
|
||||
.PHONY = all clean format debug run
|
||||
|
||||
all: $(DEST)
|
||||
|
||||
# Link all objects together into one binary
|
||||
$(DEST): ${OBJS}
|
||||
$(LD) ${LD_FLAGS} $^ $(LIBS) -o $(DEST)
|
||||
|
||||
# For every c file, compile it to an object file
|
||||
%.o: %.c
|
||||
$(CC) ${CC_FLAGS} -c $< -o $@
|
||||
|
||||
# Run the formatter
|
||||
format:
|
||||
clang-format -i src/*.c include/*.h
|
||||
|
||||
# Run the created binary
|
||||
run: $(DEST)
|
||||
./$(DEST)
|
||||
|
||||
# Run the created binary using the debugger
|
||||
debug: $(DEST)
|
||||
gdb ./$(DEST) -ex "set disassembly-flavor intel"
|
||||
|
||||
# Clean the project of all build files
|
||||
clean:
|
||||
$(info [INFO] Cleaning directory)
|
||||
rm -rf ${OBJS} $(DEST)
|
20
README.md
Normal file
20
README.md
Normal file
@ -0,0 +1,20 @@
|
||||
# This is an example project to test if SDL2 is working on WSL2
|
||||
|
||||
## Required software
|
||||
You need to install the SDL2 development library. On Debian
|
||||
based systems such as Ubuntu or Linux Mint, you can run
|
||||
|
||||
sudo apt install libsdl2-dev
|
||||
|
||||
You will also need gcc and make to build the project and gdb
|
||||
if you want to debug the project. Further clang-formatt can
|
||||
be used to format the project.
|
||||
|
||||
These can all be installed using
|
||||
|
||||
sudo apt install build-essential gdb clang-format
|
||||
|
||||
## Building the project
|
||||
In the main directory, simply run `make` with no arguments to
|
||||
build the project. You can run `make run` to run the built project
|
||||
and you can run `make debug` to debug the project using gdb.
|
21
include/ball.h
Normal file
21
include/ball.h
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef BALL_H
|
||||
#define BALL_H
|
||||
|
||||
#define BALL_WIDTH 15
|
||||
#define BALL_SPEED 1000
|
||||
|
||||
struct ball
|
||||
{
|
||||
int x_location;
|
||||
int y_location;
|
||||
int width;
|
||||
int speed;
|
||||
float x_direction;
|
||||
float y_direction;
|
||||
};
|
||||
|
||||
struct ball* ball_init(int x_location, int y_location, int width, int speed);
|
||||
struct game_data;
|
||||
void ball_update(struct game_data* data, struct ball* ball, float dt);
|
||||
|
||||
#endif /* BALL_H */
|
19
include/game.h
Normal file
19
include/game.h
Normal file
@ -0,0 +1,19 @@
|
||||
#ifndef GAME_H
|
||||
#define GAME_H
|
||||
|
||||
struct game_data
|
||||
{
|
||||
int is_running;
|
||||
struct render_data* render_data;
|
||||
|
||||
struct player* player1;
|
||||
struct player* player2;
|
||||
struct ball* ball;
|
||||
|
||||
int window_width;
|
||||
int window_height;
|
||||
};
|
||||
|
||||
void game_start(int window_width, int window_height);
|
||||
|
||||
#endif /* GAME_H */
|
25
include/player.h
Normal file
25
include/player.h
Normal file
@ -0,0 +1,25 @@
|
||||
#ifndef PLAYER_H
|
||||
#define PLAYER_H
|
||||
|
||||
#define PLAYER_WIDTH 25
|
||||
#define PLAYER_HEIGHT 250
|
||||
#define PLAYER_SPEED 6000.0
|
||||
|
||||
struct player
|
||||
{
|
||||
int x_location;
|
||||
int y_location;
|
||||
int speed;
|
||||
};
|
||||
|
||||
enum player_move_dir
|
||||
{
|
||||
PLAYER_MOVE_UP,
|
||||
PLAYER_MOVE_DOWN,
|
||||
};
|
||||
|
||||
void player_move(struct player* player, float dt, enum player_move_dir dir);
|
||||
void player_update(struct game_data* data, struct player* player, float dt);
|
||||
struct player* player_init(int x_location, int move_limit, int speed);
|
||||
|
||||
#endif /* PLAYER_H */
|
6
include/pong_ai.h
Normal file
6
include/pong_ai.h
Normal file
@ -0,0 +1,6 @@
|
||||
#ifndef PONG_AI_H
|
||||
#define PONG_AI_H
|
||||
|
||||
void pong_ai_update(struct game_data* game, struct player* player, float dt);
|
||||
|
||||
#endif /* PONG_AI_H */
|
8
include/render.h
Normal file
8
include/render.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef RENDER_H
|
||||
#define RENDER_H
|
||||
|
||||
struct render_data;
|
||||
struct render_data* render_init(int width, int height);
|
||||
void render(struct game_data* data);
|
||||
|
||||
#endif /* RENDER_H */
|
100
src/ball.c
Normal file
100
src/ball.c
Normal file
@ -0,0 +1,100 @@
|
||||
#include <ball.h>
|
||||
#include <game.h>
|
||||
#include <player.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
struct ball* ball_init(int x_location, int y_location, int width, int speed)
|
||||
{
|
||||
struct ball* ball = malloc(sizeof(struct ball));
|
||||
memset(ball, 0, sizeof(struct ball));
|
||||
|
||||
ball->x_location = x_location;
|
||||
ball->y_location = y_location;
|
||||
ball->width = width;
|
||||
ball->speed = speed;
|
||||
ball->x_direction = 1.0;
|
||||
ball->y_direction = 1.0;
|
||||
|
||||
return ball;
|
||||
}
|
||||
|
||||
static bool check_collision(int x1, int y1, int w1, int h1, int x2, int y2,
|
||||
int w2, int h2)
|
||||
{
|
||||
// Simple AABB collision
|
||||
return (x1 < x2 + w2 && x1 + w1 > x2 && y1 < y2 + h2 && y1 + h1 > y2);
|
||||
}
|
||||
|
||||
static void collide_with_player(struct player* player, struct ball* ball)
|
||||
{
|
||||
ball->x_direction = -ball->x_direction;
|
||||
// Calculate how much drift this hit should add to the ball
|
||||
//
|
||||
// _ player->y_location
|
||||
// ¦ ¦
|
||||
// ¦ ¦ ball->y_location
|
||||
// ¦ ¦ O
|
||||
// ¦ ¦ ----- player->y_location / 2
|
||||
// ¦ ¦
|
||||
// ¦ ¦
|
||||
// ¦_¦
|
||||
//
|
||||
// The following equation will create a float between -1.0 and 1.0 where
|
||||
// 0 means the ball hit the player paddle straight on, -1.0 means the ball
|
||||
// hit the topmost point of the paddle and 1.0 means the bottommost point.
|
||||
float drift = (float)((ball->y_location + (ball->width / 2)) -
|
||||
(player->y_location + PLAYER_HEIGHT / 2)) /
|
||||
((float)PLAYER_HEIGHT / 2.0f);
|
||||
ball->y_direction += drift;
|
||||
}
|
||||
|
||||
void ball_update(struct game_data* game, struct ball* ball, float dt)
|
||||
{
|
||||
ball->x_location += ball->x_direction * (float) ball->speed * dt;
|
||||
ball->y_location += ball->y_direction * (float) ball->speed * dt;
|
||||
|
||||
// Check collisions with the top and bottom of the frame
|
||||
if (ball->y_location < 0)
|
||||
{
|
||||
// collide with top of the frame
|
||||
ball->y_direction = -ball->y_direction;
|
||||
ball->y_location = 0;
|
||||
}
|
||||
else if (ball->y_location + ball->width > game->window_height)
|
||||
{
|
||||
// collide with bottom of the frame
|
||||
ball->y_direction = -ball->y_direction;
|
||||
ball->y_location = game->window_height - ball->width;
|
||||
}
|
||||
// Check collisions with the left and right of the frame
|
||||
if (ball->x_location < 0)
|
||||
{
|
||||
// end game on the left
|
||||
game->is_running = 0;
|
||||
}
|
||||
else if (ball->x_location + ball->width > game->window_width)
|
||||
{
|
||||
// end game on the right
|
||||
game->is_running = 0;
|
||||
}
|
||||
|
||||
// Check collision with players
|
||||
struct player* p1 = game->player1;
|
||||
struct player* p2 = game->player2;
|
||||
if (check_collision(ball->x_location, ball->y_location, ball->width,
|
||||
ball->width, p1->x_location, p1->y_location,
|
||||
PLAYER_WIDTH, PLAYER_HEIGHT))
|
||||
{
|
||||
ball->x_location = p1->x_location + PLAYER_WIDTH;
|
||||
collide_with_player(p1, ball);
|
||||
}
|
||||
else if (check_collision(ball->x_location, ball->y_location, ball->width,
|
||||
ball->width, p2->x_location, p2->y_location,
|
||||
PLAYER_WIDTH, PLAYER_HEIGHT))
|
||||
{
|
||||
ball->x_location = p2->x_location - ball->width;
|
||||
collide_with_player(p2, ball);
|
||||
}
|
||||
}
|
104
src/game.c
Normal file
104
src/game.c
Normal file
@ -0,0 +1,104 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <SDL2/SDL.h>
|
||||
|
||||
#include <ball.h>
|
||||
#include <game.h>
|
||||
#include <player.h>
|
||||
#include <pong_ai.h>
|
||||
#include <render.h>
|
||||
|
||||
static void check_events(struct game_data* data, float dt)
|
||||
{
|
||||
SDL_Event event;
|
||||
while (SDL_PollEvent(&event))
|
||||
{
|
||||
switch (event.type)
|
||||
{
|
||||
case SDL_QUIT:
|
||||
printf("Exit requested\n");
|
||||
data->is_running = 0;
|
||||
break;
|
||||
case SDL_KEYDOWN:
|
||||
}
|
||||
}
|
||||
|
||||
const uint8_t* state = SDL_GetKeyboardState(NULL);
|
||||
if (state[SDL_SCANCODE_UP])
|
||||
{
|
||||
player_move(data->player1, dt, PLAYER_MOVE_DOWN);
|
||||
}
|
||||
else if (state[SDL_SCANCODE_DOWN])
|
||||
{
|
||||
player_move(data->player1, dt, PLAYER_MOVE_UP);
|
||||
}
|
||||
}
|
||||
|
||||
static void game_update(struct game_data* data, float dt)
|
||||
{
|
||||
// Check and act on events
|
||||
check_events(data, dt);
|
||||
|
||||
// Update the AI
|
||||
pong_ai_update(data, data->player2, dt);
|
||||
|
||||
// Update all players and the ball
|
||||
player_update(data, data->player1, dt);
|
||||
player_update(data, data->player2, dt);
|
||||
ball_update(data, data->ball, dt);
|
||||
}
|
||||
|
||||
static void game_loop(struct game_data* data)
|
||||
{
|
||||
printf("Starting game loop\n");
|
||||
|
||||
float dt = 0;
|
||||
while (data->is_running)
|
||||
{
|
||||
unsigned long long start = SDL_GetPerformanceCounter();
|
||||
|
||||
// Update the game and render
|
||||
game_update(data, dt);
|
||||
render(data);
|
||||
|
||||
// Calculate the frame time
|
||||
unsigned long long end = SDL_GetPerformanceCounter();
|
||||
dt = (end - start) / (float)SDL_GetPerformanceFrequency();
|
||||
|
||||
// Keep the framrate at 60 Fps
|
||||
long long delay = 16.666f - (dt * 1000.0f);
|
||||
if (delay > 0)
|
||||
SDL_Delay(delay);
|
||||
}
|
||||
|
||||
printf("Goodbye\n");
|
||||
}
|
||||
|
||||
void game_start(int window_width, int window_height)
|
||||
{
|
||||
printf("Starting game\n");
|
||||
|
||||
// Create a structure to hold all the information about the game
|
||||
struct game_data* data = malloc(sizeof(struct game_data));
|
||||
memset(data, 0, sizeof(struct game_data));
|
||||
|
||||
// Fill the game data structure with the correct values
|
||||
data->is_running = 1;
|
||||
data->window_width = window_width;
|
||||
data->window_height = window_height;
|
||||
|
||||
// Initialize SDL2
|
||||
data->render_data = render_init(window_width, window_height);
|
||||
|
||||
// Create the players and the ball
|
||||
data->player1 = player_init(15, window_height, PLAYER_SPEED);
|
||||
data->player2 = player_init(window_width - PLAYER_WIDTH - 15, window_height,
|
||||
PLAYER_SPEED);
|
||||
data->ball =
|
||||
ball_init(window_width / 2 - BALL_WIDTH,
|
||||
window_height / 2 - BALL_WIDTH, BALL_WIDTH, BALL_SPEED);
|
||||
|
||||
game_loop(data);
|
||||
}
|
40
src/main.c
Normal file
40
src/main.c
Normal file
@ -0,0 +1,40 @@
|
||||
#include <getopt.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <game.h>
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
|
||||
int window_width = 500;
|
||||
int window_height = 500;
|
||||
|
||||
const char* help =
|
||||
" -w <width> Set the width of the window, default 500\n"
|
||||
" -h <height> Set the height of the window, default 500\n";
|
||||
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "w:h:")) != -1)
|
||||
{
|
||||
switch (opt)
|
||||
{
|
||||
case 'w':
|
||||
/* Set the window width */
|
||||
window_width = atoi(optarg);
|
||||
break;
|
||||
case 'h':
|
||||
/* Set the window height */
|
||||
window_height = atoi(optarg);
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "Unknown argument '%s'\n%s", argv[optind], help);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
game_start(window_width, window_height);
|
||||
|
||||
return 0;
|
||||
}
|
42
src/player.c
Normal file
42
src/player.c
Normal file
@ -0,0 +1,42 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <game.h>
|
||||
#include <player.h>
|
||||
|
||||
void player_move(struct player* player, float dt, enum player_move_dir dir)
|
||||
{
|
||||
if (dir == PLAYER_MOVE_DOWN)
|
||||
{
|
||||
player->y_location -= (int)(player->speed * dt);
|
||||
}
|
||||
else
|
||||
{
|
||||
player->y_location += (int)(player->speed * dt);
|
||||
}
|
||||
}
|
||||
|
||||
void player_update(struct game_data* game, struct player* player, float dt)
|
||||
{
|
||||
// Constrain the players location
|
||||
if (player->y_location < 0)
|
||||
{
|
||||
player->y_location = 0;
|
||||
}
|
||||
else if (player->y_location + PLAYER_HEIGHT > game->window_height)
|
||||
{
|
||||
player->y_location = game->window_height - PLAYER_HEIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
struct player* player_init(int x_location, int move_limit, int speed)
|
||||
{
|
||||
struct player* player = malloc(sizeof(struct player));
|
||||
memset(player, 0, sizeof(struct player));
|
||||
|
||||
player->x_location = x_location;
|
||||
player->y_location = (move_limit - PLAYER_HEIGHT) / 2;
|
||||
player->speed = speed;
|
||||
|
||||
return player;
|
||||
}
|
16
src/pong_ai.c
Normal file
16
src/pong_ai.c
Normal file
@ -0,0 +1,16 @@
|
||||
#include <ball.h>
|
||||
#include <game.h>
|
||||
#include <player.h>
|
||||
|
||||
void pong_ai_update(struct game_data* game, struct player* player, float dt)
|
||||
{
|
||||
int distance = game->ball->y_location - (player->y_location + PLAYER_HEIGHT / 2);
|
||||
if (distance < -1)
|
||||
{
|
||||
player_move(player, dt, PLAYER_MOVE_DOWN);
|
||||
}
|
||||
else if (distance > 1)
|
||||
{
|
||||
player_move(player, dt, PLAYER_MOVE_UP);
|
||||
}
|
||||
}
|
70
src/render.c
Normal file
70
src/render.c
Normal file
@ -0,0 +1,70 @@
|
||||
#include <SDL2/SDL.h>
|
||||
#include <ball.h>
|
||||
#include <game.h>
|
||||
#include <player.h>
|
||||
|
||||
struct render_data
|
||||
{
|
||||
SDL_Renderer* renderer;
|
||||
SDL_Window* window;
|
||||
};
|
||||
|
||||
struct render_data* render_init(int width, int height)
|
||||
{
|
||||
// Initialize SDL2
|
||||
struct render_data* data = malloc(sizeof(struct render_data));
|
||||
memset(data, 0, sizeof(struct render_data));
|
||||
|
||||
printf("Start initializing SDL2\n");
|
||||
if (SDL_Init(SDL_INIT_EVERYTHING) != 0)
|
||||
fprintf(stderr, "Could not initialize SDL2: '%s'\n", SDL_GetError());
|
||||
|
||||
data->window =
|
||||
SDL_CreateWindow("Pong", 50, 50, width, height, SDL_WINDOW_SHOWN);
|
||||
if (!data->window)
|
||||
fprintf(stderr, "Could not create window: '%s'\n", SDL_GetError());
|
||||
|
||||
data->renderer =
|
||||
SDL_CreateRenderer(data->window, -1, SDL_RENDERER_PRESENTVSYNC);
|
||||
|
||||
printf("Finished initializing SDL2\n");
|
||||
return data;
|
||||
}
|
||||
|
||||
static void player_render(struct player* player, struct render_data* data)
|
||||
{
|
||||
SDL_Rect rect;
|
||||
rect.x = player->x_location;
|
||||
rect.y = player->y_location;
|
||||
rect.w = PLAYER_WIDTH;
|
||||
rect.h = PLAYER_HEIGHT;
|
||||
|
||||
SDL_SetRenderDrawColor(data->renderer, 0xFF, 0xFF, 0xFF, 0xFF);
|
||||
SDL_RenderDrawRect(data->renderer, &rect);
|
||||
}
|
||||
|
||||
static void ball_render(struct ball* ball, struct render_data* data)
|
||||
{
|
||||
SDL_Rect rect;
|
||||
rect.x = ball->x_location;
|
||||
rect.y = ball->y_location;
|
||||
rect.w = ball->width;
|
||||
rect.h = ball->width;
|
||||
|
||||
SDL_SetRenderDrawColor(data->renderer, 0xFF, 0xFF, 0xFF, 0xFF);
|
||||
SDL_RenderDrawRect(data->renderer, &rect);
|
||||
}
|
||||
|
||||
void render(struct game_data* game)
|
||||
{
|
||||
// Clear the screen of the game
|
||||
SDL_SetRenderDrawColor(game->render_data->renderer, 0, 0, 0, 0xFF);
|
||||
SDL_RenderClear(game->render_data->renderer);
|
||||
|
||||
player_render(game->player1, game->render_data);
|
||||
player_render(game->player2, game->render_data);
|
||||
ball_render(game->ball, game->render_data);
|
||||
|
||||
// Show the newly rendered screen
|
||||
SDL_RenderPresent(game->render_data->renderer);
|
||||
}
|
Loading…
Reference in New Issue
Block a user