commit f3fdc205176881e8c2cb70fa29cdb14d4e9711ef
parent 853c85033deae647c94b87865c023c6be59abc44
Author: Henry Wilson <henry@henryandlizzy.uk>
Date: Mon, 17 Jul 2023 22:19:15 +0100
sdl: Add SDL2 example
Diffstat:
M | Tupfile | | | 1 | + |
A | src/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();
+}