examples

Toy examples in single C files.
git clone git://henryandlizzy.uk/examples
Log | Files | Refs

commit f3fdc205176881e8c2cb70fa29cdb14d4e9711ef
parent 853c85033deae647c94b87865c023c6be59abc44
Author: Henry Wilson <henry@henryandlizzy.uk>
Date:   Mon, 17 Jul 2023 22:19:15 +0100

sdl: Add SDL2 example

Diffstat:
MTupfile | 1+
Asrc/sdl.cpp | 372+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 373 insertions(+), 0 deletions(-)

diff --git a/Tupfile b/Tupfile @@ -10,6 +10,7 @@ LDLIBS_gl-3d = -lglfw -lGL -lm LDLIBS_gl-lighting = -lglfw -lGL -lm LDLIBS_gl-asteroids = -lglfw -lGL -lm -lasound LDLIBS_io_uring = -luring +LDLIBS_sdl = -lSDL2 LDLIBS_pulse-async-client = -lpulse LDLIBS_pulse-simple-client = -lpulse-simple -lm LDLIBS_sqlite-saveload = -lsqlite3 diff --git a/src/sdl.cpp b/src/sdl.cpp @@ -0,0 +1,372 @@ +#include <SDL2/SDL.h> + +int screen_width = 800; +int screen_height = 450; + +#define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) +#define ARRAY_SSIZE(a) ((ssize_t)ARRAY_SIZE(a)) + +SDL_Renderer* renderer; +SDL_Window* window; + +void initSDL(void) +{ + int rendererFlags, windowFlags; + + rendererFlags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC; + + windowFlags = 0; + + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER) < 0) + { + printf("Couldn't initialize SDL: %s\n", SDL_GetError()); + exit(1); + } + + window = SDL_CreateWindow("Pong", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, screen_width, screen_height, windowFlags); + + if (!window) + { + printf("Failed to open %d x %d window: %s\n", screen_width, screen_height, SDL_GetError()); + exit(1); + } + + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); + + renderer = SDL_CreateRenderer(window, -1, rendererFlags); + + if (!renderer) + { + printf("Failed to create renderer: %s\n", SDL_GetError()); + exit(1); + } +} + +static struct rgb +{ + unsigned char r,g,b; +} player_colours[] = { + {255,0,0}, + {0,255,0}, + {32,32,255}, + {192,192,0}, + {192,0,192}, + {0,192,255}, +}; + +struct coord +{ + int x, y; + coord operator -() const { return {-x, -y}; } + coord operator +(coord rhs) const { return {x+rhs.x, y+rhs.y}; } + coord operator -(coord rhs) const { return {x-rhs.x, y-rhs.y}; } + coord operator *(int rhs) const { return {x*rhs, y*rhs}; } + coord operator /(int rhs) const { return {x/rhs, y/rhs}; } + coord& operator +=(coord rhs) { return *this = *this + rhs; } + coord& operator -=(coord rhs) { return *this = *this - rhs; } + explicit operator bool() const { return x or y; } +}; + +static struct player +{ + SDL_GameController* controller; + struct coord pos, vel; + unsigned cooldown; + int shooting; + unsigned health; +} players[ARRAY_SIZE(player_colours)]; + +struct bullet +{ + struct player* player; + struct coord pos, vel, control; + unsigned ttl; +}; + +struct bullet bullets[128]; + +int clamp(int v, int l, int h) +{ + if (v < l) + v = l; + else if (v > h) + v = h; + return v; +} + +void shoot(struct player* p) +{ + p->cooldown = 30; + for (unsigned i = 0; i < ARRAY_SIZE(bullets); ++i) + { + struct bullet* b = bullets + i; + if (b->player) + continue; + b->player = p; + b->ttl = 4*60; + b->pos = p->pos; + b->vel = p->vel*2; + return; + } +} + +int abs(int v) +{ + return v < 0 ? -v : v; +} + +int colliding(struct coord a, struct coord b, int r) +{ + a -= b; + a.x = abs(a.x) < r; + a.y = abs(a.y) < r; + return a.x && a.y; +} + +void simulate(void) +{ + for (unsigned i = 0; i < ARRAY_SIZE(bullets); ++i) + { + struct bullet* b = bullets + i; + if (!b->player) + continue; + struct player* p = NULL; + for (unsigned i = 0; i < ARRAY_SIZE(player_colours); ++i) + { + if (players + i == b->player) + continue; + if (!players[i].controller) + continue; + if (!colliding(players[i].pos, b->pos, (2 + 5 + players[i].health) << 13)) + continue; + p = players + i; + break; + } + if (!p) + continue; + p->health -= !!p->health; + p->pos += b->vel * 10; + b->player = NULL; + } + + for (unsigned i = 0; i < ARRAY_SIZE(player_colours); ++i) + { + struct player* const p = players + i; + if (!p->controller) + continue; + p->pos += p->vel; + p->pos.x = clamp(p->pos.x, -(screen_width << 12), screen_width << 12); + p->pos.y = clamp(p->pos.y, -(screen_height << 12), screen_height << 12); + if (!p->cooldown) + { + if (p->shooting) + shoot(p); + } + else + --p->cooldown; + } + for (unsigned i = 0; i < ARRAY_SIZE(bullets); ++i) + { + struct bullet* b = bullets + i; + if (!b->player) + continue; + b->pos += b->vel; + if (b->ttl--) + continue; + b->player = NULL; + } +} + +void prepareScene(void) +{ + SDL_SetRenderDrawColor(renderer, 32, 32, 32, 255); + SDL_RenderClear(renderer); + + for (unsigned i = 0; i < ARRAY_SIZE(bullets); ++i) + { + struct bullet const* b = bullets + i; + if (!b->player) + continue; + struct rgb c = player_colours[b->player - players]; + coord pos{screen_width / 2 + (b->pos.x >> 13), screen_height / 2 + (b->pos.y >> 13)}; + SDL_Rect rect{pos.x - 3, pos.y - 3, 6, 6}; + SDL_SetRenderDrawColor(renderer, c.r, c.g, c.b, 255); + SDL_RenderDrawRect(renderer, &rect); + if (auto vel = b->vel / 512) + SDL_RenderDrawLine(renderer, pos.x, pos.y, pos.x + vel.x, pos.y + vel.y); + } + + for (unsigned i = 0; i < ARRAY_SIZE(player_colours); ++i) + { + struct player const* const p = players + i; + struct rgb const c = player_colours[i]; + if (!p->controller) + continue; + SDL_SetRenderDrawColor(renderer, c.r, c.g, c.b, 255); + int size = 5 + p->health; + coord pos{screen_width / 2 + (p->pos.x >> 13), screen_height / 2 + (p->pos.y >> 13)}; + SDL_Rect rect{pos.x - size, pos.y - size, 2*size, 2*size}; + SDL_RenderDrawRect(renderer, &rect); + if (auto vel = p->vel / 512) + SDL_RenderDrawLine(renderer, pos.x, pos.y, pos.x + vel.x, pos.y + vel.y); + } +} + +void presentScene(void) +{ + SDL_RenderPresent(renderer); +} + +char run = 1; + +void key(SDL_KeyboardEvent* e) +{ + switch (e->keysym.sym) + { + case SDLK_ESCAPE: + run = 0; + return; + } +} + +void con_add(SDL_ControllerDeviceEvent* e) +{ + char const* name = SDL_GameControllerNameForIndex(e->which); + unsigned i = 0; + for (i = 0; i < ARRAY_SIZE(players); ++i) + if (!players[i].controller) + goto allocated; + printf("Controller %d '%s' has no free slot\n", e->which, name); + return; +allocated: + players[i].controller = SDL_GameControllerOpen(i); + SDL_GameControllerSetPlayerIndex(players[i].controller, i); + players[i].vel = (struct coord){0, 0}; + players[i].pos = (struct coord){(((int)i-3)*100) << 13, 0}; + players[i].health = 10; + printf("Controller %d %p '%s' added as player %d\n", e->which, players[i].controller, name, i); +} + +void con_del(SDL_ControllerDeviceEvent* e) +{ + SDL_GameController* c = SDL_GameControllerFromInstanceID(e->which); + if (!c) + { + printf("Controller %d disconnected\n", e->which); + return; + } + + for (unsigned i = 0; i < ARRAY_SIZE(players); ++i) + if (players[i].controller == c) + { + players[i].controller = NULL; + printf("Controller %d %p disconnected, player %d removed\n", e->which, c, i); + break; + } + + SDL_GameControllerClose(c); +} + +void button(SDL_ControllerButtonEvent* e) +{ + SDL_GameController* c = SDL_GameControllerFromInstanceID(e->which); + int pid = SDL_GameControllerGetPlayerIndex(c); + if (pid < 0 || pid >= ARRAY_SSIZE(players)) + return; + + switch (e->button) + { + case SDL_CONTROLLER_BUTTON_A: + players[pid].shooting = e->state; + return; + } + + if (e->state) + printf("D[%d] P[%d] B[%u] %u\n", e->which, pid, e->button, e->state); + +} + +void axis(SDL_ControllerAxisEvent* e) +{ + SDL_GameController* c = SDL_GameControllerFromInstanceID(e->which); + int pid = SDL_GameControllerGetPlayerIndex(c); + if (pid < 0 || pid >= ARRAY_SSIZE(players)) + return; + struct player* p = players + pid; + if (!p->controller) + return; + switch (e->axis) + { + case SDL_CONTROLLER_AXIS_LEFTX: + p->vel.x = e->value >> 1; + return; + case SDL_CONTROLLER_AXIS_LEFTY: + p->vel.y = e->value >> 1; + return; + default: + printf("D[%d] P[%d] A[%u] %d\n", e->which, pid, e->axis, e->value); + } +} + +void window_event(SDL_WindowEvent const* e) +{ + switch (e->event) + { + case SDL_WINDOWEVENT_SIZE_CHANGED: + screen_width = e->data1; + screen_height = e->data2; + return; + } +} + +void doInput() +{ + SDL_Event event; + while (SDL_PollEvent(&event)) + { + switch (event.type) + { + case SDL_QUIT: + run = 0; + break; + case SDL_KEYDOWN: + key(&event.key); + break; + case SDL_CONTROLLERDEVICEADDED: + con_add(&event.cdevice); + break; + case SDL_CONTROLLERDEVICEREMOVED: + con_del(&event.cdevice); + break; + case SDL_CONTROLLERBUTTONDOWN: + case SDL_CONTROLLERBUTTONUP: + button(&event.cbutton); + break; + + case SDL_CONTROLLERAXISMOTION: + axis(&event.caxis); + break; + + case SDL_WINDOWEVENT: + window_event(&event.window); + break; + + default: + break; + } + } +} + +int main() +{ + initSDL(); + while (run) + { + doInput(); + simulate(); + prepareScene(); + presentScene(); + //SDL_Delay(16); + } + SDL_Quit(); +}