diff options
author | banana <delgadillo.tyler@gmail.com> | 2024-03-22 13:46:36 -0700 |
---|---|---|
committer | banana <delgadillo.tyler@gmail.com> | 2024-03-22 13:46:36 -0700 |
commit | ad22993557296b02bc0c947cb3bb6247fac8f47f (patch) | |
tree | 2c83ab589acab8a5074583d625d54ec44677e5d0 |
-rw-r--r-- | Makefile | 14 | ||||
-rw-r--r-- | color.h | 4 | ||||
-rw-r--r-- | constants.h | 40 | ||||
-rw-r--r-- | game_time.c | 21 | ||||
-rw-r--r-- | game_time.h | 2 | ||||
-rw-r--r-- | gfx/90.png | bin | 0 -> 725 bytes | |||
-rw-r--r-- | initialize.c | 40 | ||||
-rw-r--r-- | initialize.h | 2 | ||||
-rw-r--r-- | input.c | 59 | ||||
-rw-r--r-- | input.h | 1 | ||||
-rw-r--r-- | main.c | 25 | ||||
-rw-r--r-- | sound.c | 20 | ||||
-rw-r--r-- | sound.h | 2 | ||||
-rw-r--r-- | sound/beep_lo.wav | bin | 0 -> 17904 bytes | |||
-rw-r--r-- | structs.h | 18 | ||||
-rw-r--r-- | tennis.c | 228 | ||||
-rw-r--r-- | tennis.h | 1 | ||||
-rw-r--r-- | title.c | 3 | ||||
-rw-r--r-- | title.h | 1 |
19 files changed, 481 insertions, 0 deletions
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 @@ -0,0 +1,4 @@ +#include <SDL2/SDL.h> + +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 <SDL2/SDL.h> +#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 Binary files differnew file mode 100644 index 0000000..f329b37 --- /dev/null +++ b/gfx/90.png 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<stdio.h> +#include <SDL2/SDL.h> +#include <SDL2/SDL_mixer.h> +#include <SDL2/SDL_image.h> +#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); @@ -0,0 +1,59 @@ +#include <SDL2/SDL.h> +#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; + } + } +} @@ -0,0 +1 @@ +void process_input(); @@ -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; +} @@ -0,0 +1,20 @@ +#include <SDL2/SDL_mixer.h> + +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"); +} @@ -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 Binary files differnew file mode 100644 index 0000000..e1dc0da --- /dev/null +++ b/sound/beep_lo.wav 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 <stdint.h> +#include <SDL2/SDL.h> + +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 <SDL2/SDL.h> +#include <SDL2/SDL_image.h> + +#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(); @@ -0,0 +1,3 @@ +void init_title(void) { + +} @@ -0,0 +1 @@ +void init_title(void); |