From ad22993557296b02bc0c947cb3bb6247fac8f47f Mon Sep 17 00:00:00 2001 From: banana Date: Fri, 22 Mar 2024 13:46:36 -0700 Subject: Initial commit --- Makefile | 14 ++++ color.h | 4 + constants.h | 40 ++++++++++ game_time.c | 21 +++++ game_time.h | 2 + gfx/90.png | Bin 0 -> 725 bytes initialize.c | 40 ++++++++++ initialize.h | 2 + input.c | 59 ++++++++++++++ input.h | 1 + main.c | 25 ++++++ sound.c | 20 +++++ sound.h | 2 + sound/beep_lo.wav | Bin 0 -> 17904 bytes structs.h | 18 +++++ tennis.c | 228 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ tennis.h | 1 + title.c | 3 + title.h | 1 + 19 files changed, 481 insertions(+) create mode 100644 Makefile create mode 100644 color.h create mode 100644 constants.h create mode 100644 game_time.c create mode 100644 game_time.h create mode 100644 gfx/90.png create mode 100644 initialize.c create mode 100644 initialize.h create mode 100644 input.c create mode 100644 input.h create mode 100644 main.c create mode 100644 sound.c create mode 100644 sound.h create mode 100644 sound/beep_lo.wav create mode 100644 structs.h create mode 100644 tennis.c create mode 100644 tennis.h create mode 100644 title.c create mode 100644 title.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..67d4d0f --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +CC=gcc +CFLAGS=-Wall +LFLAGS=-lSDL2 -lSDL2_mixer -lSDL2_image +OBJS= main.o tennis.o input.o initialize.o game_time.o sound.o + +tennis: $(OBJS) + $(CC) $(LFLAGS) $^ -o $@ + +$(OBJ):%o:main.c tennis.c input.c initialize.c game_time.c sound.c structs.h constants.h + $(CC) $(CFLAGS) $^ -o $@ + +.PHONY: +clean: + rm ./*.o tennis diff --git a/color.h b/color.h new file mode 100644 index 0000000..fcdcc13 --- /dev/null +++ b/color.h @@ -0,0 +1,4 @@ +#include + +const SDL_Color COLOR_BG = { 0x1C, 0x1C, 0x1C, 0xFF }; +const SDL_Color COLOR_FG = { 0xEB, 0xDB, 0xB2, 0xFF }; diff --git a/constants.h b/constants.h new file mode 100644 index 0000000..4e44ec2 --- /dev/null +++ b/constants.h @@ -0,0 +1,40 @@ +#define GAME_TITLE "pong" + +#define FALSE 0 +#define TRUE 1 + +#define WINDOW_WIDTH 800 +#define WINDOW_HEIGHT 600 + +#define FPS 30 + +#define FRAME_TARGET_TIME (1000 / FPS) + +#define INPUT_RESET 0b10000000 +#define PADDLE_1_UP 0b00001000 +#define PADDLE_1_DOWN 0b00000100 +#define PADDLE_2_UP 0b00000010 +#define PADDLE_2_DOWN 0b00000001 + +#define CENTER_LINE_WIDTH WINDOW_WIDTH / 100 +#define CENTER_LINE_HEIGHT WINDOW_HEIGHT / 8 + +#define SCORE_TALLY_WIDTH WINDOW_WIDTH / 100 +#define SCORE_TALLY_HEIGHT WINDOW_HEIGHT / 16 +#define PLAYER1_SCORE_TALLY_X WINDOW_WIDTH * 0.25 - CENTER_LINE_WIDTH / 2 +#define PLAYER2_SCORE_TALLY_X WINDOW_WIDTH * 0.75 - CENTER_LINE_WIDTH / 2 +#define SCORE_TALLY_Y SCORE_TALLY_HEIGHT + +#define BALL_RADIUS WINDOW_WIDTH / 50 +#define BALL_INIT_X WINDOW_WIDTH / 2 - BALL_RADIUS / 2 +#define BALL_INIT_Y WINDOW_HEIGHT / 2 - BALL_RADIUS / 2 +#define BALL_INIT_SPEED 300 + +#define PADDLE_WIDTH WINDOW_WIDTH / 50 +#define PADDLE_HEIGHT WINDOW_HEIGHT / 4 +#define PADDLE1_X PADDLE_WIDTH * 2 +#define PADDLE2_X WINDOW_WIDTH - PADDLE_WIDTH * 3 +#define PADDLE_Y WINDOW_HEIGHT / 2 - PADDLE_HEIGHT / 2 +#define PADDLE_SPEED WINDOW_HEIGHT / 2 + +#define MAX_SND_CHANNELS 1 diff --git a/game_time.c b/game_time.c new file mode 100644 index 0000000..af116b6 --- /dev/null +++ b/game_time.c @@ -0,0 +1,21 @@ +#include +#include "./constants.h" + +int last_frame_time = 0; + +void delay(void) { + // logic to keep a fixed timestep + int time_to_wait = FRAME_TARGET_TIME - (SDL_GetTicks() - last_frame_time); + + if(time_to_wait > 0 && time_to_wait <= FRAME_TARGET_TIME) { + SDL_Delay(time_to_wait); + } +} + +float get_delta_time() { + // get a delta time factor converted to seconds + float delta_time = (SDL_GetTicks() - last_frame_time) / 1000.0f; + + last_frame_time = SDL_GetTicks(); + return delta_time; +} diff --git a/game_time.h b/game_time.h new file mode 100644 index 0000000..1cdbbc8 --- /dev/null +++ b/game_time.h @@ -0,0 +1,2 @@ +void delay(void); +float get_delta_time(); diff --git a/gfx/90.png b/gfx/90.png new file mode 100644 index 0000000..f329b37 Binary files /dev/null and b/gfx/90.png differ diff --git a/initialize.c b/initialize.c new file mode 100644 index 0000000..75d9e2d --- /dev/null +++ b/initialize.c @@ -0,0 +1,40 @@ +#include +#include +#include +#include +#include "./constants.h" +#include "./structs.h" + +int initialize_window(App* app) { + if(SDL_Init(SDL_INIT_EVERYTHING) != 0) { + fprintf(stderr, "Error initializing SDL.\n"); + return FALSE; + } + app->window = SDL_CreateWindow( + GAME_TITLE, + SDL_WINDOWPOS_CENTERED, + SDL_WINDOWPOS_CENTERED, + WINDOW_WIDTH, + WINDOW_HEIGHT, + SDL_WINDOW_BORDERLESS + ); + if(!app->window) { + fprintf(stderr, "Error creating SDL_Window.\n"); + return FALSE; + } + app->renderer = SDL_CreateRenderer(app->window, -1, 0); + if(!app->renderer) { + fprintf(stderr, "Error creating SDL_Renderer"); + return FALSE; + } + IMG_Init(IMG_INIT_PNG); + Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 1024); + Mix_AllocateChannels(MAX_SND_CHANNELS); + return TRUE; +} + +void destroy_window(App *app) { + SDL_DestroyRenderer(app->renderer); + SDL_DestroyWindow(app->window); + SDL_Quit(); +} diff --git a/initialize.h b/initialize.h new file mode 100644 index 0000000..7461c9c --- /dev/null +++ b/initialize.h @@ -0,0 +1,2 @@ +int initialize_window(App* app); +void destroy_window(App *app); diff --git a/input.c b/input.c new file mode 100644 index 0000000..6e9994d --- /dev/null +++ b/input.c @@ -0,0 +1,59 @@ +#include +#include "./constants.h" +#include "./structs.h" + +extern App app; + +static void handle_key_down(SDL_KeyboardEvent *event) { + if(event->repeat == 0) { + if(event->keysym.sym == SDLK_ESCAPE) { + app.input |= INPUT_RESET; + } + if(event->keysym.sym == SDLK_a) { + app.input |= PADDLE_1_UP; + } + if(event->keysym.sym == SDLK_z) { + app.input |= PADDLE_1_DOWN; + } + if(event->keysym.sym == SDLK_QUOTE) { + app.input |= PADDLE_2_UP; + } + if(event->keysym.sym == SDLK_SLASH) { + app.input |= PADDLE_2_DOWN; + } + } +} + +static void handle_key_up(SDL_KeyboardEvent *event) { + if(event->repeat == 0) { + if(event->keysym.sym == SDLK_a) { + app.input ^= PADDLE_1_UP; + } + if(event->keysym.sym == SDLK_z) { + app.input ^= PADDLE_1_DOWN; + } + if(event->keysym.sym == SDLK_QUOTE) { + app.input ^= PADDLE_2_UP; + } + if(event->keysym.sym == SDLK_SLASH) { + app.input ^= PADDLE_2_DOWN; + } + } +} + +void process_input() { + SDL_Event event; + while(SDL_PollEvent(&event)) { + switch(event.type) { + case SDL_QUIT: + app.input |= INPUT_RESET; + break; + case SDL_KEYDOWN: + handle_key_down(&event.key); + break; + case SDL_KEYUP: + handle_key_up(&event.key); + break; + } + } +} diff --git a/input.h b/input.h new file mode 100644 index 0000000..1f7d71e --- /dev/null +++ b/input.h @@ -0,0 +1 @@ +void process_input(); diff --git a/main.c b/main.c new file mode 100644 index 0000000..1483f89 --- /dev/null +++ b/main.c @@ -0,0 +1,25 @@ +#include "./structs.h" +#include "./constants.h" +#include "./input.h" +#include "./initialize.h" +#include "./tennis.h" +#include "./title.h" + +App app; + +int main(int argc, char* argv[]) { + + memset(&app, 0, sizeof(app)); + + initialize_window(&app); + + init_pong(); + + while(app.input ^ INPUT_RESET) { + process_input(); + app.update(); + app.render(); + } + destroy_window(&app); + return 0; +} diff --git a/sound.c b/sound.c new file mode 100644 index 0000000..b58a39b --- /dev/null +++ b/sound.c @@ -0,0 +1,20 @@ +#include + +static void load_sounds(void); + +static Mix_Chunk* sounds[2]; + +void init_sounds(void) { + memset(sounds, 0, sizeof(Mix_Chunk *) * 2); + + load_sounds(); +} + +void play_sound(int id, int channel) { + Mix_PlayChannel(channel, sounds[id], 0); +} + +static void load_sounds(void) { + sounds[0] = Mix_LoadWAV("sound/beep_lo.wav"); + sounds[1] = Mix_LoadWAV("sound/beep_hi.wav"); +} diff --git a/sound.h b/sound.h new file mode 100644 index 0000000..cc49590 --- /dev/null +++ b/sound.h @@ -0,0 +1,2 @@ +void init_sounds(void); +void play_sound(int id, int channel); diff --git a/sound/beep_lo.wav b/sound/beep_lo.wav new file mode 100644 index 0000000..e1dc0da Binary files /dev/null and b/sound/beep_lo.wav differ diff --git a/structs.h b/structs.h new file mode 100644 index 0000000..4dc6207 --- /dev/null +++ b/structs.h @@ -0,0 +1,18 @@ +#include +#include + +typedef struct { + SDL_Window* window; + SDL_Renderer* renderer; + uint8_t input; + + void (*update)(void); + void (*render)(void); +} App; + +typedef struct { + SDL_Rect rect; + int dx; + int dy; + float timer; +} game_object; diff --git a/tennis.c b/tennis.c new file mode 100644 index 0000000..7147073 --- /dev/null +++ b/tennis.c @@ -0,0 +1,228 @@ +#include +#include + +#include "./constants.h" +#include "./structs.h" +#include "./game_time.h" +#include "./sound.h" +#include "./color.h" + +extern App app; + +game_object ball; +game_object paddle1; +game_object paddle2; + +int player1_score; +int player2_score; + +float speed_up_factor; +float delta_time; + +float serve_timer; +float collision_timer; +float winner_timer; + +void init_pong(); +void init_ball(); +void init_paddles(); + +void update(); +void update_ball(float dt); +void do_ball_collision(game_object* paddle); +void update_paddles(float dt); +void do_serve(void); +void do_goal(int* score_to_update); +void do_winner(void); + +void render(); +void draw_center_line(); +void draw_score(); + +void init_pong() { + app.input = 0; + player1_score = 0; + player2_score = 0; + init_ball(); + init_paddles(); + init_sounds(); + app.update = update; + app.render = render; +} + +void init_ball() { + const game_object BALL_TEMPLATE = { + { + BALL_INIT_X, + BALL_INIT_Y, + BALL_RADIUS, + BALL_RADIUS + }, + 0, + 0, + 0 + }; + ball = BALL_TEMPLATE; + speed_up_factor = -1.05; +} + +void init_paddles() { + const game_object PADDLE_TEMPLATE = { + { + 0, + PADDLE_Y, + PADDLE_WIDTH, + PADDLE_HEIGHT + }, + 0, + 0, + 0 + }; + paddle1 = paddle2 = PADDLE_TEMPLATE; + paddle1.rect.x = PADDLE1_X; + paddle2.rect.x = PADDLE2_X; +} + +void do_ball_collision(game_object* paddle) { + if(ball.timer >= 1) { + ball.dx *= speed_up_factor; + ball.dy = (WINDOW_HEIGHT * 0.015) * -((paddle->rect.y + paddle->rect.h / 2) - ball.rect.y); + play_sound(0, -1); + ball.timer = 0; + } +} + +void update_ball(float dt) { + // serve ball if not moving + if( ball.dx == 0 ) { do_serve(); } + // check for collision with paddles + if(SDL_HasIntersection(&ball.rect, &paddle1.rect)) { do_ball_collision(&paddle1); } + if(SDL_HasIntersection(&ball.rect, &paddle2.rect)) { do_ball_collision(&paddle2); } + // check for collision with top and bottom of screen + if(ball.rect.y + ball.rect.h >= WINDOW_HEIGHT || ball.rect.y <= 0) { ball.dy *= -1; } + // check for exit off right or left of screen + if(ball.rect.x > WINDOW_WIDTH) { do_goal(&player1_score); } + if(ball.rect.x + ball.rect.w < 0) { do_goal(&player2_score); } + // update ball's position + ball.rect.x += ball.dx * dt; + ball.rect.y += ball.dy * dt; +} + +void update_paddles(float dt) { + // reset paddles' movement vectors, adjust according to input + paddle1.dy = 0; + paddle2.dy = 0; + + if(app.input & PADDLE_1_UP && paddle1.rect.y >= 0) { paddle1.dy = -PADDLE_SPEED; } + if(app.input & PADDLE_1_DOWN && + paddle1.rect.y + paddle1.rect.h <= WINDOW_HEIGHT) { paddle1.dy = PADDLE_SPEED; } + if(app.input & PADDLE_2_UP && paddle2.rect.y >= 0) { paddle2.dy = -PADDLE_SPEED; } + if(app.input & PADDLE_2_DOWN && + paddle2.rect.y + paddle2.rect.h <= WINDOW_HEIGHT) { paddle2.dy = PADDLE_SPEED; } + + // update paddles' positions + paddle1.rect.y += paddle1.dy * dt; + paddle2.rect.y += paddle2.dy * dt; + + ball.timer += dt; +} + +void do_goal(int* score_to_update) { + ++*score_to_update; + init_ball(); + init_paddles(); + if(*score_to_update >= 5) { do_winner(); } +} + +void do_winner() { + init_pong(); + + if(player1_score > player2_score) { + SDL_Log("PLAYER 1 WINS!!!"); + } + else { + SDL_Log("PLAYER 2 WINS!!!"); + } +} + +void do_serve(void) { + if(ball.timer >= 3) { + if (SDL_GetTicks() % 2 == 0) { + ball.dx = BALL_INIT_SPEED; + } + else { + ball.dx = -BALL_INIT_SPEED; + } + } +} + +void update() { + delay(); + delta_time = get_delta_time(); + + update_ball(delta_time); + update_paddles(delta_time); +} + +void draw_center_line(void) { + for(int i = -CENTER_LINE_HEIGHT / 2; i < WINDOW_HEIGHT; i += CENTER_LINE_HEIGHT * 2) { + SDL_Rect center_line_rect = { + WINDOW_WIDTH / 2 - CENTER_LINE_WIDTH / 2, + i, + CENTER_LINE_WIDTH, + CENTER_LINE_HEIGHT + }; + SDL_RenderFillRect(app.renderer, ¢er_line_rect); + } +} + +void draw_score(void) { + for(int i = 0; i != player1_score; ++i) { + SDL_Rect score_tally_rect = { + PLAYER1_SCORE_TALLY_X + i * WINDOW_WIDTH / 16, + SCORE_TALLY_Y, + SCORE_TALLY_WIDTH, + SCORE_TALLY_HEIGHT + }; + SDL_RenderFillRect(app.renderer, &score_tally_rect); + } + for(int i = 0; i != player2_score; ++i) { + SDL_Rect score_tally_rect = { + PLAYER2_SCORE_TALLY_X - i * WINDOW_WIDTH / 16, + SCORE_TALLY_Y, + SCORE_TALLY_WIDTH, + SCORE_TALLY_HEIGHT + }; + SDL_RenderFillRect(app.renderer, &score_tally_rect); + } +} + +void draw_message(void) { + SDL_Texture* font = IMG_LoadTexture(app.renderer, "./gfx/90.png"); + SDL_RenderCopy(app.renderer, font, NULL, NULL); +} + + +void render(void) { + // fill screen + SDL_SetRenderDrawColor(app.renderer, COLOR_BG.r, COLOR_BG.g, COLOR_BG.b, COLOR_BG.a); + SDL_RenderClear(app.renderer); + + // draw ball, paddles + SDL_SetRenderDrawColor(app.renderer, COLOR_FG.r, COLOR_FG.g, COLOR_FG.b, COLOR_FG.a); + SDL_RenderFillRect(app.renderer, &ball.rect); + SDL_RenderFillRect(app.renderer, &paddle1.rect); + SDL_RenderFillRect(app.renderer, &paddle2.rect); + + // draw center line, dividing the play field + draw_center_line(); + + // draw score tally + draw_score(); + + // draw serve, goal, or winner message + // draw_message(); + + // present rendered scene + SDL_RenderPresent(app.renderer); +} diff --git a/tennis.h b/tennis.h new file mode 100644 index 0000000..e8da64d --- /dev/null +++ b/tennis.h @@ -0,0 +1 @@ +void init_pong(); diff --git a/title.c b/title.c new file mode 100644 index 0000000..f57b29e --- /dev/null +++ b/title.c @@ -0,0 +1,3 @@ +void init_title(void) { + +} diff --git a/title.h b/title.h new file mode 100644 index 0000000..ad56dc5 --- /dev/null +++ b/title.h @@ -0,0 +1 @@ +void init_title(void); -- cgit v1.2.3