Banjo API 1.0.0-rc.2
Low-level C99 game development API
Loading...
Searching...
No Matches
physics_kinematics.c

2D kinematics for projectile motion simulation.

2D kinematics for projectile motion simulation.Kinematics calculates motion from initial conditions (position, velocity) and constant acceleration (like gravity). Use bj_compute_kinematics_2d() for:

  • Projectiles: thrown objects, bullets, jumping characters
  • Falling objects under gravity
  • Any motion with constant acceleration

The kinematics equation: position = p0 + v0 * t + 0.5 * a * t^2 This gives exact positions at any time t without step-by-step integration. Much more accurate than repeatedly adding velocity each frame.

#include <banjo/app.h>
#include <banjo/main.h>
#include <banjo/draw.h>
#include <banjo/event.h>
#include <banjo/log.h>
#include <banjo/pixel.h>
#include <banjo/system.h>
#include <banjo/time.h>
#include <banjo/vec.h>
#include <banjo/window.h>
#include <stdlib.h>
#include <time.h>
#define SCREEN_WIDTH 800
#define SCREEN_HEIGHT 600
// Framebuffer pixel mode. Used with bj_get_pixel_value so we can
// pre-compute colours at setup before the first draw callback fires
// (when the framebuffer would otherwise be reachable).
#define FB_PIXEL_MODE BJ_PIXEL_MODE_XRGB8888
#define BALLS_LEN 1000
#define BALLS_RADIUS BJ_F(3.0)
#define GRAVITY BJ_F(50.0)
// Each ball tracks its initial conditions and elapsed time. Kinematics needs:
// - initial_position (shared by all balls here)
// - initial_velocity (unique per ball)
// - acceleration (gravity, shared)
// - time_alive (how long since launch)
// From these, we can compute exact position at any time.
struct {
bj_vec2 initial_velocity;
bj_vec2 position;
uint32_t color;
double time_alive;
// Setting up a projectile: choose initial velocity based on desired direction
// and speed. For realistic projectile arcs, use angle and magnitude.
// Angle: -90° points straight up, 0° points right (here: -90° to 0° for fountain effect)
// Magnitude: initial speed in pixels/second
static void reset_ball(size_t at) {
const uint8_t r = (uint8_t)(128 + rand() % 128);
const uint8_t g = (uint8_t)(128 + rand() % 128);
const uint8_t b = (uint8_t)(128 + rand() % 128);
switch (rand() % 3) {
case 0: balls[at].color = bj_get_pixel_value(FB_PIXEL_MODE, 100, g, b); break;
case 1: balls[at].color = bj_get_pixel_value(FB_PIXEL_MODE, r, 100, b); break;
default: balls[at].color = bj_get_pixel_value(FB_PIXEL_MODE, r, g, 100); break;
}
// Random angle from -90° to 0° (straight up to straight right).
const bj_real angle_rand = (bj_real)rand() / (bj_real)RAND_MAX;
const bj_real angle = -BJ_PI / BJ_F(2.0) + angle_rand * (BJ_PI / BJ_F(2.0));
// Random speed between 100 and 200 pixels/second.
const bj_real magnitude = BJ_F(100.0) + ((bj_real)rand() / (bj_real)RAND_MAX) * BJ_F(100.0);
// Convert angle + magnitude to velocity vector (vx, vy).
balls[at].initial_velocity.x = bj_cos(angle) * magnitude;
balls[at].initial_velocity.y = bj_sin(angle) * magnitude;
balls[at].time_alive = 0;
}
static void initialize_balls() {
// Gravity acceleration: (0, 50) means 50 pixels/second² downward.
// Positive y is down in screen coordinates, so gravity pulls objects down.
// Adjust this value to make physics feel heavier or lighter.
// All balls start from the bottom-left corner.
for(size_t b = 0 ; b < BALLS_LEN ; ++b) {
}
}
// Update physics: accumulate time and compute exact positions using kinematics.
// dt is delta time (seconds since last frame) for frame-rate independence.
static void update(bj_real dt) {
for(size_t b = 0 ; b < BALLS_LEN ; ++b) {
// Track total time since this ball was launched.
balls[b].time_alive += dt;
// bj_compute_kinematics_2d() computes position from:
// - initial_position: where the ball started
// - initial_velocity: how fast it was moving at launch
// - gravity: constant acceleration
// - time_alive: elapsed time since launch
// Returns exact position using: p = p₀ + v₀*t + ½*a*t²
// This is more accurate than integrating velocity frame-by-frame.
balls[b].initial_velocity,
balls[b].time_alive
);
const bj_real x = balls[b].position.x;
const bj_real y = balls[b].position.y;
// Reset balls that leave the screen.
) {
}
}
}
static void draw(bj_bitmap* framebuffer) {
bj_clear_bitmap(framebuffer);
for(size_t b = 0 ; b < BALLS_LEN ; ++b) {
bj_draw_filled_circle(framebuffer,
balls[b].position.x, balls[b].position.y, BALLS_RADIUS,
balls[b].color
);
}
}
static void on_draw(
struct bj_window* w,
struct bj_render_target* target,
const struct bj_rect* dirty,
void* user_data
) {
(void)w; (void)dirty; (void)user_data;
}
static void* setup(struct bj_app* app, void* init_data) {
(void)init_data;
srand((unsigned)time(NULL));
bj_quit_app(app, 1);
return 0;
}
window = bj_bind_window("2D Kinematics", 100, 100, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0);
return 0;
}
// Physics runs at the fixed-step rate (60 Hz by default) regardless of
// how often step gets to fire. tick.delta is constant (1 / rate), which
// keeps projectile motion identical across machines.
static void fixed_step(struct bj_app* app, struct bj_tick_info tick, void* user_data) {
(void)app; (void)user_data;
update(tick.delta);
}
static void step(struct bj_app* app, struct bj_tick_info tick, void* user_data) {
(void)tick; (void)user_data;
bj_quit_app(app, 0);
}
}
static void teardown(struct bj_app* app, void* user_data) {
(void)user_data;
bj_end();
}
int main(int argc, char* argv[]) {
(void)argc; (void)argv;
}
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
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.
bj_real delta
Definition app.h:107
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.
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_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.
#define BJ_PI
PI in the selected bj_real precision.
Definition math.h:111
#define bj_sin
Sine.
Definition math.h:246
#define bj_cos
Cosine.
Definition math.h:237
#define BJ_F(x)
Literal suffix helper for bj_real when float is selected.
Definition math.h:78
float bj_real
Selected real type for float configuration.
Definition math.h:76
Axis-aligned rectangle: a top-left corner plus a width and height.
Definition rect.h:33
2D vector of bj_real components.
Definition vec.h:51
struct bj_vec2 bj_compute_kinematics_2d(struct bj_vec2 position, struct bj_vec2 velocity, struct bj_vec2 acceleration, bj_real time)
Integrate constant-acceleration 2D kinematics: position at time t.
uint32_t bj_get_pixel_value(enum bj_pixel_mode mode, uint8_t red, uint8_t green, uint8_t blue)
Returns an opaque value representing a pixel colour, given its RGB composition.
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
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.
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.
static void fixed_step(struct bj_app *app, struct bj_tick_info tick, void *ud)
Logging utility functions.
Portable main() replacement with platform-aware entry shim.
Physics helpers (SI units, but dimensionally consistent with any unit system).
#define SCREEN_WIDTH
#define BALLS_RADIUS
bj_vec2 gravity
static void initialize_balls()
#define BALLS_LEN
#define GRAVITY
#define SCREEN_HEIGHT
#define FB_PIXEL_MODE
static void reset_ball(size_t at)
struct @075336127262265255345326341123106263042221024322 balls[1000]
static void update(bj_real dt)
bj_vec2 initial_position
Header file for general pixel manipulation facilities.
Header file for system interactions.
Header file for time manipulation utilities.
Fixed-size vector types (2D, 3D, 4D) and inline operations.
Header file for bj_window type.