Banjo API 1.0.0-rc.2
Low-level C99 game development API
Loading...
Searching...
No Matches
pong.c
Go to the documentation of this file.
1
103
104#include <banjo/app.h>
105#include <banjo/main.h>
106#include <banjo/bitmap.h>
107#include <banjo/draw.h>
108#include <banjo/event.h>
109#include <banjo/log.h>
110#include <banjo/memory.h>
111#include <banjo/rect.h>
112#include <banjo/system.h>
113#include <banjo/time.h>
114#include <banjo/window.h>
115
116#define W 800
117#define H 600
118#define PAD_W 16
119#define PAD_H 80
120#define PAD_MARGIN 32
121#define BALL_R 8
122#define PAD_SPEED 360.0f /* pixels per second */
123#define BALL_SPEED 280.0f /* pixels per second (initial) */
124#define BOUNCE_SPEEDUP 1.05f /* paddle hit accelerates the ball */
125#define MAX_DT 0.05f /* clamp delta to avoid teleporting */
126
136
137static void reset_ball(pong_t* p, int last_winner) {
138 p->ball_x = (float)W * 0.5f;
139 p->ball_y = (float)H * 0.5f;
140 p->ball_vx = (last_winner >= 0 ? +BALL_SPEED : -BALL_SPEED);
141 p->ball_vy = (last_winner & 1) ? BALL_SPEED * 0.6f
142 : -BALL_SPEED * 0.6f;
143}
144
145static void update(pong_t* p, float dt) {
146 /* --- Paddle input: continuous-state polling -------------------- */
147 if (bj_get_key(p->window, BJ_KEY_W) == BJ_PRESS) p->pad_left_y -= PAD_SPEED * dt;
148 if (bj_get_key(p->window, BJ_KEY_S) == BJ_PRESS) p->pad_left_y += PAD_SPEED * dt;
151
152 /* --- Clamp paddles to the playfield ---------------------------- */
153 if (p->pad_left_y < 0) p->pad_left_y = 0;
154 if (p->pad_left_y > H - PAD_H) p->pad_left_y = (float)(H - PAD_H);
155 if (p->pad_right_y < 0) p->pad_right_y = 0;
156 if (p->pad_right_y > H - PAD_H) p->pad_right_y = (float)(H - PAD_H);
157
158 /* --- Ball ------------------------------------------------------ */
159 p->ball_x += p->ball_vx * dt;
160 p->ball_y += p->ball_vy * dt;
161
162 /* Top / bottom walls bounce */
163 if (p->ball_y < BALL_R) {
164 p->ball_y = BALL_R;
165 p->ball_vy = -p->ball_vy;
166 }
167 if (p->ball_y > H - BALL_R) {
168 p->ball_y = (float)(H - BALL_R);
169 p->ball_vy = -p->ball_vy;
170 }
171
172 /* Left paddle */
173 if (p->ball_vx < 0 &&
174 p->ball_x - BALL_R < PAD_MARGIN + PAD_W &&
175 p->ball_y > p->pad_left_y &&
176 p->ball_y < p->pad_left_y + PAD_H) {
177 p->ball_x = (float)(PAD_MARGIN + PAD_W + BALL_R);
178 p->ball_vx = -p->ball_vx * BOUNCE_SPEEDUP;
179 }
180 /* Right paddle */
181 if (p->ball_vx > 0 &&
182 p->ball_x + BALL_R > W - PAD_MARGIN - PAD_W &&
183 p->ball_y > p->pad_right_y &&
184 p->ball_y < p->pad_right_y + PAD_H) {
185 p->ball_x = (float)(W - PAD_MARGIN - PAD_W - BALL_R);
186 p->ball_vx = -p->ball_vx * BOUNCE_SPEEDUP;
187 }
188
189 /* Score (ball escaped past a paddle): reset toward the loser */
190 if (p->ball_x < -BALL_R) {
191 reset_ball(p, +1);
192 } else if (p->ball_x > W + BALL_R) {
193 reset_ball(p, -1);
194 }
195}
196
197static void draw(pong_t* p, bj_bitmap* fb) {
198 bj_clear_bitmap(fb);
199
200 const uint32_t white = bj_make_bitmap_pixel(fb, 0xFF, 0xFF, 0xFF);
201 const uint32_t gray = bj_make_bitmap_pixel(fb, 0x40, 0x40, 0x40);
202
203 /* Dashed centre line */
204 for (int16_t y = 0; y < H; y = (int16_t)(y + 24)) {
205 bj_rect dash = { .x = (int16_t)(W / 2 - 2), .y = y, .w = 4, .h = 12 };
206 bj_draw_filled_rectangle(fb, &dash, gray);
207 }
208
209 /* Paddles */
210 bj_rect lpad = { .x = PAD_MARGIN, .y = (int16_t)p->pad_left_y,
211 .w = PAD_W, .h = PAD_H };
212 bj_rect rpad = { .x = (int16_t)(W - PAD_MARGIN - PAD_W), .y = (int16_t)p->pad_right_y,
213 .w = PAD_W, .h = PAD_H };
214 bj_draw_filled_rectangle(fb, &lpad, white);
215 bj_draw_filled_rectangle(fb, &rpad, white);
216
217 /* Ball */
218 bj_draw_filled_circle(fb, (int)p->ball_x, (int)p->ball_y, BALL_R, white);
219}
220
221static void on_draw(
222 struct bj_window* window,
223 struct bj_render_target* target,
224 const struct bj_rect* dirty,
225 void* user_data
226) {
227 (void)window; (void)dirty;
228 draw((pong_t*)user_data, bj_render_target_bitmap(target));
229}
230
231static void* setup(struct bj_app* app, void* init_data) {
232 (void)init_data;
233 pong_t* p = bj_calloc(sizeof(pong_t));
234
235 if (!bj_begin(BJ_VIDEO_SYSTEM, 0)) {
236 bj_err("pong: video init failed");
237 bj_free(p);
238 bj_quit_app(app, 1);
239 return 0;
240 }
241
242 p->window = bj_bind_window("Pong - W/S vs Up/Down (ESC to quit)",
243 100, 100, W, H, 0, 0);
246
247 p->pad_left_y = (float)(H - PAD_H) * 0.5f;
248 p->pad_right_y = (float)(H - PAD_H) * 0.5f;
249 reset_ball(p, 0);
250 return p;
251}
252
253static void step(struct bj_app* app, struct bj_tick_info tick, void* user_data) {
254 (void)tick;
255 pong_t* p = (pong_t*)user_data;
256
257 const float dt = (float)bj_step_delay_stopwatch(&p->sw);
258 const float clamped_dt = dt > MAX_DT ? MAX_DT : dt;
259
260 update(p, clamped_dt);
263
265 bj_quit_app(app, 0);
266 }
267}
268
269static void teardown(struct bj_app* app, void* user_data) {
270 (void)app;
271 pong_t* p = (pong_t*)user_data;
273 bj_end();
274 bj_free(p);
275}
276
277int main(int argc, char* argv[]) {
278 (void)argc; (void)argv;
279 return bj_run_app(setup, step, 0, teardown, 0);
280}
Application lifecycle: callback-driven setup, step, and teardown.
int main(int argc, char *argv[])
Definition audio_pcm.c:177
static void step(struct bj_app *app, struct bj_tick_info tick, void *user_data)
Definition audio_pcm.c:144
static void teardown(struct bj_app *app, void *user_data)
Definition audio_pcm.c:170
static void * setup(struct bj_app *app, void *init_data)
Definition audio_pcm.c:107
Header file for Bitmap type.
static void on_draw(struct bj_window *w, struct bj_render_target *target, const struct bj_rect *dirty, void *user_data)
Definition bitmap_blit.c:32
bj_window * window
Definition bitmap_blit.c:24
Header file for Bitmap drawing functions.
void draw(bj_bitmap *bmp)
Definition drawing_2d.c:133
Sytem event management API.
int bj_run_app(bj_app_setup_fn setup, bj_app_step_fn step, bj_app_fixed_step_fn fixed_step, bj_app_teardown_fn teardown, void *init_data)
Drive the application lifecycle.
void bj_quit_app(struct bj_app *app, int exit_code)
Signal the given application to exit on the next iteration.
Timing snapshot handed to the step and fixed-step callbacks.
Definition app.h:106
void bj_clear_bitmap(struct bj_bitmap *bitmap)
Fills the entire bitmap with the clear colour.
uint32_t bj_make_bitmap_pixel(struct bj_bitmap *bitmap, uint8_t red, uint8_t green, uint8_t blue)
Returns an opaque value representing a pixel colour, given its RGB composition.
struct bj_render_target bj_render_target
Definition api.h:346
struct bj_bitmap bj_bitmap
Definition api.h:328
struct bj_window bj_window
Definition api.h:354
void bj_draw_filled_rectangle(struct bj_bitmap *bitmap, const struct bj_rect *area, uint32_t pixel)
Draws a filled rectangle in the given bitmap.
void bj_draw_filled_circle(struct bj_bitmap *bitmap, int cx, int cy, int radius, uint32_t color)
Draw a filled circle onto a bitmap.
void bj_dispatch_events(void)
Poll and dispatch all pending events.
void bj_close_on_escape(struct bj_window *window, const struct bj_key_event *event, void *user_data)
Handle the ESC key to close a window.
bj_key_callback_fn bj_set_key_callback(bj_key_callback_fn callback, void *user_data)
Set the global callback for keyboard key events.
@ BJ_KEY_S
S key.
Definition event.h:226
@ BJ_KEY_DOWN
Down arrow key.
Definition event.h:189
@ BJ_KEY_W
W key.
Definition event.h:230
@ BJ_KEY_UP
Up arrow key.
Definition event.h:187
@ BJ_PRESS
The key or button was pressed.
Definition event.h:392
#define bj_err(...)
Log a message using the BJ_LOG_ERROR level.
Definition log.h:169
Axis-aligned rectangle: a top-left corner plus a width and height.
Definition rect.h:33
void * bj_calloc(size_t size)
Allocate size bytes of zero-initialised memory.
void bj_free(void *memory)
Free a previously allocated memory block.
bj_bool bj_begin(int systems, struct bj_error **error)
Initialises the system.
void bj_end(void)
De-initialises the system.
@ BJ_VIDEO_SYSTEM
Definition system.h:81
double bj_step_delay_stopwatch(struct bj_stopwatch *stopwatch)
Steps the stopwatch and returns the delay since the previous step.
Structure representing a simple stopwatch.
Definition time.h:150
void bj_set_draw_callback(struct bj_window *window, bj_window_draw_fn fn, void *user_data)
Register the redraw callback for window.
static void bj_invalidate_window(struct bj_window *window)
Mark the whole window as needing a repaint.
Definition window.h:470
struct bj_window * bj_bind_window(const char *title, uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t flags, struct bj_error **error)
Create a new struct bj_window with the specified attributes.
int bj_get_key(const struct bj_window *window, int key)
Query the current state of a key for a given window.
struct bj_bitmap * bj_render_target_bitmap(struct bj_render_target *target)
Reach the software framebuffer behind a render target.
bj_bool bj_should_close_window(struct bj_window *window)
Get the close flag state of a window.
void bj_unbind_window(struct bj_window *window)
Deletes a struct bj_window object and releases associated memory.
#define BALL_R
Logging utility functions.
Portable main() replacement with platform-aware entry shim.
All memory-related functions, including custom allocators.
static void reset_ball(size_t at)
static void update(bj_real dt)
float ball_vx
Definition pong.c:134
#define PAD_W
Definition pong.c:118
#define BOUNCE_SPEEDUP
Definition pong.c:124
#define BALL_SPEED
Definition pong.c:123
bj_stopwatch sw
Definition pong.c:129
float pad_right_y
Definition pong.c:132
#define PAD_MARGIN
Definition pong.c:120
bj_window * window
Definition pong.c:128
#define W
Definition pong.c:116
float ball_x
Definition pong.c:133
float ball_y
Definition pong.c:133
float ball_vy
Definition pong.c:134
float pad_left_y
Definition pong.c:131
#define PAD_H
Definition pong.c:119
#define H
Definition pong.c:117
#define MAX_DT
Definition pong.c:125
#define PAD_SPEED
Definition pong.c:122
Definition pong.c:127
Axis-aligned rectangle in pixel coordinates.
Header file for system interactions.
Header file for time manipulation utilities.
Header file for bj_window type.