summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--game/src/plugins/CMakeLists.txt18
-rw-r--r--game/src/plugins/pong.c237
2 files changed, 252 insertions, 3 deletions
diff --git a/game/src/plugins/CMakeLists.txt b/game/src/plugins/CMakeLists.txt
index ecb2a45..e5abbb8 100644
--- a/game/src/plugins/CMakeLists.txt
+++ b/game/src/plugins/CMakeLists.txt
@@ -2,16 +2,28 @@ cmake_minimum_required(VERSION 3.0)
2 2
3project(plugins) 3project(plugins)
4 4
5set(LINK_LIBRARIES cstring math gfx) 5set(LINK_LIBRARIES cstring math gfx gfx-app)
6
7# GLTF viewer
6 8
7add_library(gltf_view SHARED 9add_library(gltf_view SHARED
8 gltf_view.c) 10 gltf_view.c)
9 11
12target_link_libraries(gltf_view PUBLIC
13 ${LINK_LIBRARIES})
14
15# Texture viewer
16
10add_library(texture_view SHARED 17add_library(texture_view SHARED
11 texture_view.c) 18 texture_view.c)
12 19
13target_link_libraries(gltf_view PUBLIC 20target_link_libraries(texture_view PUBLIC
14 ${LINK_LIBRARIES}) 21 ${LINK_LIBRARIES})
15 22
16target_link_libraries(texture_view PUBLIC 23# Pong
24
25add_library(pong SHARED
26 pong.c)
27
28target_link_libraries(pong PUBLIC
17 ${LINK_LIBRARIES}) 29 ${LINK_LIBRARIES})
diff --git a/game/src/plugins/pong.c b/game/src/plugins/pong.c
new file mode 100644
index 0000000..3a0b825
--- /dev/null
+++ b/game/src/plugins/pong.c
@@ -0,0 +1,237 @@
1#include "plugin.h"
2
3#include <gfx/gfx.h>
4#include <gfx/gfx_app.h>
5#include <gfx/renderer.h>
6
7#include <math/mat4.h>
8#include <math/vec2.h>
9#include <math/vec4.h>
10
11#include <stdlib.h>
12
13static const vec2 PAD_SIZE = (vec2){120, 20};
14static const R PLAYER_Y_OFFSET = 50;
15static const R PLAYER_SPEED = 800;
16
17static const R ENEMY_SPEED = 2;
18
19static const R BALL_SIZE = 18;
20static const R BALL_SPEED = 360; // In each dimension.
21
22static const R EPS = (R)1e-3;
23
24typedef struct Player {
25 vec2 position;
26} Player;
27
28typedef struct Ball {
29 vec2 position;
30 vec2 velocity;
31} Ball;
32
33typedef struct State {
34 bool game_started;
35 Player human;
36 Player enemy;
37 Ball ball;
38 mat4 viewProjection;
39} State;
40
41bool init(Game* game, State** pp_state) {
42 assert(game);
43
44 State* state = calloc(1, sizeof(State));
45 if (!state) {
46 return false;
47 }
48
49 *pp_state = state;
50 return true;
51
52cleanup:
53 free(state);
54 return false;
55}
56
57void shutdown(Game* game, State* state) {
58 assert(game);
59 assert(state);
60}
61
62static void move_ball(Ball* ball, R dt, int width, int height) {
63 assert(ball);
64
65 const R offset = BALL_SIZE / 2;
66
67 ball->position = vec2_add(ball->position, vec2_scale(ball->velocity, dt));
68
69 // Right wall.
70 if (ball->position.x + offset > (R)width) {
71 ball->position.x = (R)width - offset - EPS;
72 ball->velocity.x = -ball->velocity.x;
73 }
74 // Left wall.
75 else if (ball->position.x - offset < 0) {
76 ball->position.x = offset + EPS;
77 ball->velocity.x = -ball->velocity.x;
78 }
79 // Top wall.
80 if (ball->position.y + offset > (R)height) {
81 ball->position.y = (R)height - offset - EPS;
82 ball->velocity.y = -ball->velocity.y;
83 }
84 // Bottom wall.
85 else if (ball->position.y - offset < 0) {
86 ball->position.y = offset + EPS;
87 ball->velocity.y = -ball->velocity.y;
88 }
89}
90
91void move_enemy_player(int width, Player* player, R t) {
92 const R half_width = (R)width / 2;
93 const R amplitude = half_width - (PAD_SIZE.x / 2);
94 player->position.x = half_width + amplitude * sinf(t * ENEMY_SPEED);
95}
96
97void move_human_player(Player* player, R dt) {
98 assert(player);
99
100 R speed = 0;
101 if (gfx_is_key_pressed('a')) {
102 speed -= PLAYER_SPEED;
103 }
104 if (gfx_is_key_pressed('d')) {
105 speed += PLAYER_SPEED;
106 }
107
108 player->position.x += speed * dt;
109}
110
111void clamp_player(Player* player, int width) {
112 assert(player);
113
114 const R offset = PAD_SIZE.x / 2;
115
116 // Left wall.
117 if (player->position.x + offset > (R)width) {
118 player->position.x = (R)width - offset;
119 }
120 // Right wall.
121 else if (player->position.x - offset < 0) {
122 player->position.x = offset;
123 }
124}
125
126void collide_ball(vec2 old_ball_position, const Player* player, Ball* ball) {
127 assert(player);
128 assert(ball);
129
130 // Discrete but simple collision. Checks for intersection and moves the ball
131 // back by a small epsilon.
132
133 // Player bounding box.
134 const vec2 player_pmin = vec2_make(
135 player->position.x - PAD_SIZE.x / 2, player->position.y - PAD_SIZE.y / 2);
136 const vec2 player_pmax = vec2_make(
137 player->position.x + PAD_SIZE.x / 2, player->position.y + PAD_SIZE.y / 2);
138
139 // Ball bounding box.
140 const vec2 ball_pmin = vec2_make(
141 ball->position.x - BALL_SIZE / 2, ball->position.y - BALL_SIZE / 2);
142 const vec2 ball_pmax = vec2_make(
143 ball->position.x + BALL_SIZE / 2, ball->position.y + BALL_SIZE / 2);
144
145 // Check for intersection and update ball.
146 if (!((ball_pmax.x < player_pmin.x) || (ball_pmin.x > player_pmax.x) ||
147 (ball_pmax.y < player_pmin.y) || (ball_pmin.y > player_pmax.y))) {
148 ball->position =
149 vec2_add(old_ball_position, vec2_scale(ball->velocity, -EPS));
150 ball->velocity.y = -ball->velocity.y;
151 }
152}
153
154void update(Game* game, State* state, double t, double dt) {
155 assert(game);
156 assert(state);
157
158 // TODO: Move game width/height to GfxApp query functions?
159 const vec2 old_ball_position = state->ball.position;
160 move_ball(&state->ball, (R)dt, game->width, game->height);
161 move_human_player(&state->human, (R)dt);
162 move_enemy_player(game->width, &state->enemy, (R)t);
163 clamp_player(&state->human, game->width);
164 collide_ball(old_ball_position, &state->human, &state->ball);
165 collide_ball(old_ball_position, &state->enemy, &state->ball);
166}
167
168static void draw_player(ImmRenderer* imm, const Player* player) {
169 assert(imm);
170 assert(player);
171
172 const vec2 half_box = vec2_div(PAD_SIZE, vec2_make(2, 2));
173
174 const vec2 pmin = vec2_sub(player->position, half_box);
175 const vec2 pmax = vec2_add(player->position, half_box);
176 const aabb2 box = aabb2_make(pmin, pmax);
177
178 gfx_imm_draw_aabb2(imm, box);
179}
180
181static void draw_ball(ImmRenderer* imm, const Ball* ball) {
182 assert(imm);
183 assert(ball);
184
185 const vec2 half_box = vec2_make(BALL_SIZE / 2, BALL_SIZE / 2);
186 const vec2 pmin = vec2_sub(ball->position, half_box);
187 const vec2 pmax = vec2_add(ball->position, half_box);
188 const aabb2 box = aabb2_make(pmin, pmax);
189
190 gfx_imm_draw_aabb2(imm, box);
191}
192
193void render(const Game* game, const State* state) {
194 assert(game);
195 assert(state);
196
197 ImmRenderer* imm = gfx_get_imm_renderer(game->gfx);
198 gfx_imm_start(imm);
199 gfx_imm_set_view_projection_matrix(imm, &state->viewProjection);
200 gfx_imm_load_identity(imm);
201 gfx_imm_set_colour(imm, vec4_make(1, 1, 1, 1));
202 draw_player(imm, &state->human);
203 draw_player(imm, &state->enemy);
204 draw_ball(imm, &state->ball);
205 gfx_imm_end(imm);
206}
207
208static R clamp_to_width(int width, R x, R extent) {
209 return min(x, (R)width - extent);
210}
211
212void resize(Game* game, State* state, int width, int height) {
213 assert(game);
214 assert(state);
215
216 state->viewProjection = mat4_ortho(0, (R)width, 0, (R)height, -1, 1);
217
218 state->human.position.y = PLAYER_Y_OFFSET;
219 state->enemy.position.y = (R)height - PLAYER_Y_OFFSET;
220
221 if (!state->game_started) {
222 state->human.position.x = (R)width / 2;
223 state->enemy.position.x = (R)width / 2;
224
225 state->ball.position =
226 vec2_div(vec2_make((R)width, (R)height), vec2_make(2, 2));
227
228 state->ball.velocity = vec2_make(BALL_SPEED, BALL_SPEED);
229
230 state->game_started = true;
231 } else {
232 state->human.position.x =
233 clamp_to_width(width, state->human.position.x, PAD_SIZE.x / 2);
234 state->enemy.position.x =
235 clamp_to_width(width, state->enemy.position.x, PAD_SIZE.x / 2);
236 }
237}