summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile14
-rw-r--r--color.h4
-rw-r--r--constants.h40
-rw-r--r--game_time.c21
-rw-r--r--game_time.h2
-rw-r--r--gfx/90.pngbin0 -> 725 bytes
-rw-r--r--initialize.c40
-rw-r--r--initialize.h2
-rw-r--r--input.c59
-rw-r--r--input.h1
-rw-r--r--main.c25
-rw-r--r--sound.c20
-rw-r--r--sound.h2
-rw-r--r--sound/beep_lo.wavbin0 -> 17904 bytes
-rw-r--r--structs.h18
-rw-r--r--tennis.c228
-rw-r--r--tennis.h1
-rw-r--r--title.c3
-rw-r--r--title.h1
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
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 <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
new file mode 100644
index 0000000..f329b37
--- /dev/null
+++ b/gfx/90.png
Binary files 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<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);
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 <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;
+ }
+ }
+}
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 <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");
+}
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
--- /dev/null
+++ b/sound/beep_lo.wav
Binary files 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 <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, &center_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);