commit 1d0d2b0851188b6be9f63ad55cb3aebd365097f8 Author: igor Date: Mon Nov 6 19:25:02 2023 -0800 Initial commit. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..4e4ac93 --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ +CC=clang +FLAGS=-Wall -Wextra -g +LIBS=-I/opt/homebrew/include -I/opt/homebrew/include/SDL2 -L/opt/homebrew/lib -lSDL2 -lSDL2_ttf + +all: clean build + +build: pingpong + +pingpong: pingpong.c + ${CC} ${FLAGS} ${LIBS} pingpong.c -o pingpong + +clean: + rm -rf pingpong.dSYM pingpong + +.PHONY: build clean diff --git a/iosevka-bold.ttf b/iosevka-bold.ttf new file mode 100644 index 0000000..c4a0b7c Binary files /dev/null and b/iosevka-bold.ttf differ diff --git a/pingpong.c b/pingpong.c new file mode 100644 index 0000000..c7707ec --- /dev/null +++ b/pingpong.c @@ -0,0 +1,317 @@ +#include +#include +#include + +#define TRUE 1 +#define FALSE 0 + +#define WINDOW_WIDTH 800 +#define WINDOW_HEIGHT 600 + +#define FPS 120 +#define TARGET_FRAME_TIME (1000 / FPS) + +typedef struct game_state { + int is_running; + int last_frame_time; + + struct ball { + float x; + float y; + float width; + float height; + float acceleration_x; + float acceleration_y; + } ball; + + struct player { + float x; + float y; + float width; + float height; + float acceleration_y; + int score; + int up; + int down; + } player_one, player_two; +} game_state; + +void initialize_window(SDL_Window **window, SDL_Renderer **renderer) +{ + if(SDL_Init(SDL_INIT_EVERYTHING) != 0) { + fprintf(stderr, "Error initializing SDL.\n"); + exit(1); + } + + *window = SDL_CreateWindow( + NULL, + SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, + WINDOW_WIDTH, WINDOW_HEIGHT, + SDL_WINDOW_SHOWN); + if(!window) { + fprintf(stderr, "Error creating SDL Window.\n"); + exit(1); + } + + *renderer = SDL_CreateRenderer(*window, -1, 0); + if(!renderer) { + fprintf(stderr, "Error creating SDL Renderer.\n"); + exit(1); + } +} + +void initialized_font(TTF_Font **font) +{ + if(TTF_Init() < 0) { + fprintf(stderr, "Error opening font.\n"); + exit(1); + } + + *font = TTF_OpenFont("./iosevka-bold.ttf", 36); + if(!font) { + fprintf(stderr, "Error opening font.\n"); + exit(1); + } +} + +void handle_ball(game_state *state, float delta_time) +{ + if(state->ball.x < state->player_one.x + state->player_one.width && + (state->ball.y < state->player_one.y + state->player_one.height && + state->ball.y > state->player_one.y)) + { + state->ball.x = state->player_one.x + state->player_one.width; + state->ball.acceleration_x *= -1; + } + + if(state->ball.x + state->ball.width > state->player_two.x && + (state->ball.y < state->player_two.y + state->player_two.height && + state->ball.y > state->player_two.y)) + { + state->ball.x = state->player_two.x - state->ball.width; + state->ball.acceleration_x *= -1; + } + + if(state->ball.x <= 0) { + state->ball.x = WINDOW_WIDTH / 2; + state->ball.y = 0; + state->player_two.score += 1; + if(state->player_two.score > 5) { + state->player_two.score = 1; + state->player_one.score = 0; + } + } + if(state->ball.x >= WINDOW_WIDTH - state->ball.width) { + state->ball.x = WINDOW_WIDTH / 2; + state->ball.y = 0; + state->player_one.score += 1; + if(state->player_one.score > 5) { + state->player_one.score = 1; + state->player_two.score = 0; + } + } + + if(state->ball.y < 0) { + state->ball.acceleration_y *= -1; + state->ball.y = 0; + } + if(state->ball.y > WINDOW_HEIGHT - state->ball.height) { + state->ball.acceleration_y *= -1; + state->ball.y = WINDOW_HEIGHT - state->ball.height; + } + + state->ball.x += state->ball.acceleration_x * delta_time; + state->ball.y += state->ball.acceleration_y * delta_time; +} + +void handle_paddles(game_state *state, float delta_time) +{ + if(state->player_one.up) + state->player_one.y -= state->player_one.acceleration_y * delta_time; + if(state->player_one.down) + state->player_one.y += state->player_one.acceleration_y * delta_time; + if(state->player_two.up) + state->player_two.y -= state->player_one.acceleration_y * delta_time; + if(state->player_two.down) + state->player_two.y += state->player_one.acceleration_y * delta_time; +} + +void render_score(game_state *state, SDL_Renderer *renderer, TTF_Font *font) +{ + /* Build score strings */ + char *score; + score = malloc(3*sizeof(char)); + score[0] = (char)(state->player_one.score + 48); /* FIX */ + score[1] = ':'; + score[2] = (char)(state->player_two.score + 48); + + /* Render */ + SDL_Surface *text; + SDL_Color color = { 198, 204, 215, 255 }; + text = TTF_RenderText_Solid(font, score, color); + if (!text) { + fprintf(stderr, "Error initializing Text.\n"); + exit(1); + } + SDL_Texture *text_texture; + text_texture = SDL_CreateTextureFromSurface(renderer, text); + SDL_Rect dest = { WINDOW_WIDTH/2-20, 10, text->w, text->h }; + SDL_RenderCopy(renderer, text_texture, NULL, &dest); + + /* free */ + SDL_DestroyTexture(text_texture); + SDL_FreeSurface(text); + free(score); +} + +void render_paddles(game_state *state, SDL_Renderer *renderer) +{ + SDL_Rect player_one_rect = { state->player_one.x, state->player_one.y, + state->player_one.width, state->player_one.height }; + SDL_Rect player_two_rect = { state->player_two.x, state->player_two.y, + state->player_two.width, state->player_two.height }; + + SDL_SetRenderDrawColor(renderer, 215, 78, 66, 255); + SDL_RenderFillRect(renderer, &player_one_rect); + + SDL_SetRenderDrawColor(renderer, 16, 133, 255, 255); + SDL_RenderFillRect(renderer, &player_two_rect); +} + +void render_ball(game_state *state, SDL_Renderer *renderer) +{ + SDL_Rect ball_rect = { state->ball.x, state->ball.y, state->ball.width, + state->ball.height }; + SDL_SetRenderDrawColor(renderer, 198, 204, 215, 255); + SDL_RenderFillRect(renderer, &ball_rect); +} + +void setup(game_state *state) +{ + state->is_running = TRUE; + state->last_frame_time = 0; + + state->ball.x = 40; + state->ball.y = 40; + state->ball.width = 15; + state->ball.height = 15; + state->ball.acceleration_x = 300; + state->ball.acceleration_y = 400; + + state->player_one.x = 10; + state->player_one.y = 10; + state->player_one.width = 10; + state->player_one.height = 100; + state->player_one.acceleration_y = 500; + state->player_one.score = 0; + state->player_one.up = FALSE; + state->player_one.down = FALSE; + + state->player_two.x = WINDOW_WIDTH - 20; + state->player_two.y = 10; + state->player_two.width = 10; + state->player_two.height = 100; + state->player_two.acceleration_y = 500; + state->player_two.score = 0; + state->player_two.up = FALSE; + state->player_two.down = FALSE; +} + +void process_input(game_state *state) +{ + SDL_Event event; + const Uint8 *currentKeyStates; + SDL_PollEvent(&event); + currentKeyStates = SDL_GetKeyboardState(NULL); + + if(currentKeyStates[SDL_SCANCODE_W]) { + state->player_one.up = TRUE; + state->player_one.down = FALSE; + } else if(currentKeyStates[SDL_SCANCODE_S]) { + state->player_one.up = FALSE; + state->player_one.down = TRUE; + } else { + state->player_one.up = FALSE; + state->player_one.down = FALSE; + } + + if(currentKeyStates[SDL_SCANCODE_UP]) { + state->player_two.up = TRUE; + state->player_two.down = FALSE; + } else if(currentKeyStates[SDL_SCANCODE_DOWN]) { + state->player_two.up = FALSE; + state->player_two.down = TRUE; + } else { + state->player_two.up = FALSE; + state->player_two.down = FALSE; + } + + switch(event.type) { + case SDL_QUIT: + state->is_running = FALSE; + break; + case SDL_KEYDOWN: + if(event.key.keysym.sym == SDLK_ESCAPE) + state->is_running = FALSE; + break; + } +} + +void update(game_state *state) +{ + int time_to_wait; + float delta_time; + + time_to_wait = TARGET_FRAME_TIME - (SDL_GetTicks() - state->last_frame_time); + + if(time_to_wait > 0 && time_to_wait <= TARGET_FRAME_TIME) + SDL_Delay(time_to_wait); + + delta_time = (SDL_GetTicks() - state->last_frame_time) / 1000.0f; + + state->last_frame_time = SDL_GetTicks(); + + handle_ball(state, delta_time); + handle_paddles(state, delta_time); +} + +void render(game_state *state, SDL_Renderer *renderer, TTF_Font *font) +{ + SDL_SetRenderDrawColor(renderer, 24, 26, 31, 255); + SDL_RenderClear(renderer); + + render_paddles(state, renderer); + render_ball(state, renderer); + render_score(state, renderer, font); + + SDL_RenderPresent(renderer); +} + +void destroy_window(SDL_Window *window, SDL_Renderer *renderer) +{ + SDL_DestroyRenderer(renderer); + SDL_DestroyWindow(window); + SDL_Quit(); +} + +int main() +{ + game_state state; + SDL_Window *window = NULL; + SDL_Renderer *renderer = NULL; + TTF_Font *font; + + initialize_window(&window, &renderer); + initialized_font(&font); + + setup(&state); + while(state.is_running) { + process_input(&state); + update(&state); + render(&state, renderer, font); + } + + destroy_window(window, renderer); + + return 0; +}