Skip to content

Instantly share code, notes, and snippets.

@jweinst1
Last active July 6, 2026 08:14
Show Gist options
  • Select an option

  • Save jweinst1/16d5bb7b0efd1279af7787041627f284 to your computer and use it in GitHub Desktop.

Select an option

Save jweinst1/16d5bb7b0efd1279af7787041627f284 to your computer and use it in GitHub Desktop.
SDL example of basic click game
#include <SDL3/SDL.h>
#include <iostream>
#include <cstdint>
#include <cassert>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <utility>
#include <algorithm>
#include <string>
#define MYGAME_SQSIZE 16
#define MYGAME_SQPWR 4
enum class PathDirect : char {
Up = 'U',
Down = 'D',
Left = 'L',
Right = 'R'
};
static constexpr inline bool float_is_even(float input) {
return std::fmod(input, 2.0f) == 0.0f;
}
struct PathExecutor {
float spriteX = 0.0f;
float spriteY = 0.0f;
float targetX = 0.0f;
float targetY = 0.0f;
float sqaureSize = 0.0f;
float increment = 0.0f;
const char* curPath = nullptr;
PathDirect lastPath = PathDirect::Down;
void setTargetDirection() {
if (curPath == nullptr) {
return;
}
PathDirect pdir = static_cast<PathDirect>(*curPath);
lastPath = pdir;
switch (pdir) {
case PathDirect::Up:
targetY = spriteY - sqaureSize;
targetX = spriteX;
break;
case PathDirect::Down:
targetY = spriteY + sqaureSize;
targetX = spriteX;
break;
case PathDirect::Left:
targetX = spriteX - sqaureSize;
targetY = spriteY;
break;
case PathDirect::Right:
targetX = spriteX + sqaureSize;
targetY = spriteY;
break;
}
}
void begin() {
setTargetDirection();
}
void moveSprite() {
// called at every frame
if (curPath == nullptr)
return;
PathDirect pdir = static_cast<PathDirect>(*curPath);
switch (pdir) {
case PathDirect::Up:
if (targetY < spriteY) {
spriteY -= increment;
return;
}
break;
case PathDirect::Down:
if (targetY > spriteY) {
spriteY += increment;
return;
}
break;
case PathDirect::Left:
if (targetX < spriteX) {
spriteX -= increment;
return;
}
break;
case PathDirect::Right:
if (targetX > spriteX) {
spriteX += increment;
return;
}
break;
}
++curPath;
if (*curPath == '\0') {
curPath = nullptr;
return;
}
// todo this lags by one iteration, think if we should fix it or not
setTargetDirection();
}
};
enum class MoveResult {
Moved,
Finished,
Blocked
};
struct PathFinder {
int* gameBoard = nullptr;
char* path = nullptr;
size_t boardSize = 0;
size_t pathLen = 0;
int boardX = -1;
int boardY = -1;
int curX = -1;
int curY = -1;
int endX = -1;
int endY = -1;
int startX = -1;
int startY = -1;
PathFinder(const int* board, int boardx, int boardy,
int startx, int starty,
int endx, int endy) {
boardX = boardx;
boardY = boardy;
boardSize = boardX * boardY;
startX = startx;
startY = starty;
endX = endx;
endY = endy;
curX = startX;
curY = startY;
gameBoard = new int[boardSize];
std::memcpy(gameBoard, board, boardSize * sizeof(int)); // copy so we can just mark in same space what is visited
path = new char[boardSize + 1];
std::memset(path, 0, boardSize + 1);
}
PathFinder(const int* board, int boardx, int boardy) {
boardX = boardx;
boardY = boardy;
boardSize = boardX * boardY;
gameBoard = new int[boardSize];
std::memcpy(gameBoard, board, boardSize * sizeof(int)); // copy so we can just mark in same space what is visited
path = new char[boardSize + 1];
std::memset(path, 0, boardSize + 1);
}
~PathFinder() {
delete[] gameBoard;
delete[] path;
}
void resetPath(const int* board, int startx, int starty,
int endx, int endy) {
startX = startx;
startY = starty;
endX = endx;
endY = endy;
curX = startX;
curY = startY;
std::memset(path, 0, boardSize + 1);
std::memcpy(gameBoard, board, boardSize * sizeof(int));
pathLen = 0;
}
static std::string createPath(const int* board, int boardx, int boardy,
int startx, int starty,
int endx, int endy) {
PathFinder finder(board, boardx, boardy, startx, starty, endx, endy);
finder.findPath();
return std::string(finder.path);
}
bool isAtEnd() const {
return endX == curX && endY == curY;
}
bool isWalkable(int x, int y) const {
return gameBoard[(y * boardX) + x] == 0;
}
void markSpotNonWalkable(int x, int y) {
gameBoard[(y * boardX) + x] = 1;
}
bool canMoveUp() const {
return curY != 0 && isWalkable(curX, curY - 1);
}
bool canMoveDown() const {
return (curY != (boardY - 1)) && isWalkable(curX, curY + 1);
}
bool canMoveLeft() const {
return curX != 0 && isWalkable(curX - 1, curY);
}
bool canMoveRight() const {
return (curX != (boardX - 1)) && isWalkable(curX + 1, curY);
}
void moveUp() {
markSpotNonWalkable(curX, curY);
--curY;
path[pathLen++] = (char)PathDirect::Up;
}
void moveDown() {
markSpotNonWalkable(curX, curY);
++curY;
path[pathLen++] = (char)PathDirect::Down;
}
void moveLeft() {
markSpotNonWalkable(curX, curY);
--curX;
path[pathLen++] = (char)PathDirect::Left;
}
void moveRight() {
markSpotNonWalkable(curX, curY);
++curX;
path[pathLen++] = (char)PathDirect::Right;
}
bool canReverse() const {
return pathLen > 0;
}
void reverse() {
printf("Reversing!\n");
markSpotNonWalkable(curX, curY);
size_t prevDirIndex = --pathLen;
char prevDir = path[prevDirIndex];
PathDirect d = static_cast<PathDirect>(prevDir);
switch (d) {
case PathDirect::Up:
curY += 1;
break;
case PathDirect::Down:
curY -= 1;
break;
case PathDirect::Left:
curX += 1;
break;
case PathDirect::Right:
curX -= 1;
break;
}
path[prevDirIndex] = '\0';
}
bool attemptAnyMove() {
if (canMoveUp()) {
moveUp();
return true;
}
if (canMoveDown()) {
moveDown();
return true;
}
if (canMoveLeft()) {
moveLeft();
return true;
}
if (canMoveRight()) {
moveRight();
return true;
}
return false;
}
MoveResult move() {
if (isAtEnd()) {
return MoveResult::Finished;
}
int xdist = endX - curX;
int ydist = endY - curY;
if (xdist > 0) {
// move right
if (canMoveRight()) {
moveRight();
return MoveResult::Moved;
}
}
if (xdist < 0) {
// move left
if (canMoveLeft()) {
moveLeft();
return MoveResult::Moved;
}
}
if (ydist > 0) {
// move down
if (canMoveDown()) {
moveDown();
return MoveResult::Moved;
}
}
if (ydist < 0) {
// move up
if (canMoveUp()) {
moveUp();
return MoveResult::Moved;
}
}
if (attemptAnyMove()) {
return MoveResult::Moved;
}
if (canReverse()) {
reverse();
return MoveResult::Moved;
}
return MoveResult::Blocked;
}
size_t findPath() {
if (isAtEnd())
return 0;
size_t foundLen = 1;
auto res = move();
while (res == MoveResult::Moved) {
++foundLen;
res = move();
}
return foundLen;
}
};
enum GroundType {
Grass = 0,
Rock = 1
};
static constexpr int GAMEBOARD[] = {
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
static constexpr size_t SQ_CAM_X = 8;
static constexpr size_t SQ_CAM_X_PX = SQ_CAM_X * MYGAME_SQSIZE;
static constexpr float SQ_CAM_X_PXF = static_cast<float>(SQ_CAM_X_PX);
static constexpr size_t SQ_CAM_Y = 8;
static constexpr size_t SQ_CAM_Y_PX = SQ_CAM_Y * MYGAME_SQSIZE;
static constexpr float SQ_CAM_Y_PXF = static_cast<float>(SQ_CAM_Y_PX);
static constexpr size_t SQ_MAX_X = 16;
static constexpr size_t SQ_MAX_X_PX = SQ_MAX_X * MYGAME_SQSIZE;
static constexpr float SQ_MAX_X_PXF = static_cast<float>(SQ_MAX_X_PX);
static constexpr size_t SQ_MAX_Y = 8;
static constexpr size_t SQ_MAX_Y_PX = SQ_MAX_Y * MYGAME_SQSIZE;
static constexpr float SQ_MAX_Y_PXF = static_cast<float>(SQ_MAX_Y_PX);
static constexpr size_t SQ_TOTAL_CNT = sizeof(GAMEBOARD) / sizeof(int);
static_assert(SQ_TOTAL_CNT == (SQ_MAX_X * SQ_MAX_Y), "Board dimension not equal to array size");
static int getGameBoardSpot(float x, float y) {
std::cout << "Got x=" << x << " y=" << y << "\n";
const unsigned xnum = static_cast<unsigned>(x) / MYGAME_SQSIZE;
const unsigned ynum = static_cast<unsigned>(y) / MYGAME_SQSIZE;
//std::cout << "Got xnum=" << xnum << " ynum=" << ynum << "\n";
return GAMEBOARD[(ynum * SQ_MAX_X) + xnum];
}
static void posToCoord(size_t pos, size_t* x, size_t* y) {
*x = pos & (SQ_MAX_X - 1);
*y = pos / SQ_MAX_X;
}
struct GameRGB {
unsigned char r = 0;
unsigned char g = 0;
unsigned char b = 0;
};
static constexpr GameRGB GRASSCOLOR{78, 176, 43};
static constexpr GameRGB ROCKCOLOR{140, 104, 11};
static constexpr GameRGB PLAYERCOLOR{236, 210, 252};
static constexpr SDL_Color PLAYER_PALT[4] = {
{ 0, 0, 0, 0 }, // Index 0: Alpha is 0 (Transparent background)
{ 0, 0, 0, 255 }, // Index 1: Opaque Black
{ 127, 127, 127, 255 }, // gray opaque
{ 255, 255, 255, 255 } // Index 2: Opaque White
};
static void canvasTranslate(float focusX, float focusY,
float screenX, float screenY,
float mapX, float mapY,
float inputX, float inputY,
float* outputX, float* outputY) {
float cameraX = focusX - (screenX / 2.0f);
float cameraY = focusY - (screenY / 2.0f);
cameraX = std::clamp(cameraX, 0.0f, mapX - screenX);
cameraY = std::clamp(cameraY, 0.0f, mapY - screenY);
*outputX = inputX - cameraX;
*outputY = inputY - cameraY;
}
static void canvasWarp(float focusX, float focusY,
float screenX, float screenY,
float mapX, float mapY,
float inputX, float inputY,
float* outputX, float* outputY) {
float cameraX = focusX - (screenX / 2.0f);
float cameraY = focusY - (screenY / 2.0f);
// Clamp the camera bounds identically to canvasTranslate
cameraX = std::clamp(cameraX, 0.0f, mapX - screenX);
cameraY = std::clamp(cameraY, 0.0f, mapY - screenY);
*outputX = inputX + cameraX;
*outputY = inputY + cameraY;
}
static constexpr uint8_t PLAYER_DOWN_S[] = {
0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0,
0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0,
0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0,
0, 0, 1, 1, 1, 2, 3, 3, 3, 3, 2, 1, 1, 1, 0, 0,
0, 0, 1, 1, 3, 1, 1, 1, 1, 1, 1, 3, 1, 1, 0, 0,
0, 1, 3, 1, 3, 3, 3, 3, 3, 3, 3, 3, 1, 3, 1, 0,
0, 1, 3, 3, 3, 3, 1, 3, 3, 1, 3, 3, 3, 3, 1, 0,
0, 0, 1, 1, 3, 3, 1, 3, 3, 1, 3, 3, 1, 1, 0, 0,
0, 0, 1, 1, 1, 3, 3, 2, 2, 3, 3, 1, 1, 1, 0, 0,
0, 1, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 1, 0,
0, 1, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 1, 0,
0, 0, 1, 1, 1, 2, 2, 1, 1, 2, 2, 1, 1, 1, 0, 0,
0, 0, 0, 1, 2, 1, 1, 2, 2, 1, 1, 2, 1, 0, 0, 0,
0, 0, 0, 1, 2, 2, 2, 1, 1, 2, 2, 2, 1, 0, 0, 0,
0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0
};
static constexpr uint8_t PLAYER_DOWN_0[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0,
0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0,
0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0,
0, 0, 1, 1, 1, 2, 3, 3, 3, 3, 2, 1, 1, 1, 0, 0,
0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
0, 1, 3, 1, 3, 3, 3, 3, 3, 3, 3, 3, 1, 3, 1, 0,
0, 1, 3, 3, 3, 3, 1, 3, 3, 1, 3, 3, 3, 3, 1, 0,
0, 1, 1, 1, 3, 3, 1, 3, 3, 1, 3, 3, 1, 1, 0, 0,
0, 1, 3, 1, 1, 3, 3, 2, 2, 3, 3, 1, 2, 1, 0, 0,
0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 0, 0,
0, 0, 0, 1, 1, 2, 1, 1, 1, 1, 3, 3, 1, 1, 0, 0,
0, 0, 0, 0, 1, 1, 1, 2, 2, 1, 3, 3, 1, 0, 0, 0,
0, 0, 0, 0, 1, 2, 2, 1, 1, 0, 1, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0
};
static constexpr uint8_t PLAYER_DOWN_1[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0,
0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0,
0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0,
0, 0, 1, 1, 1, 2, 3, 3, 3, 3, 2, 1, 1, 1, 0, 0,
0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
0, 1, 3, 1, 3, 3, 3, 3, 3, 3, 3, 3, 1, 3, 1, 0,
0, 1, 3, 3, 3, 3, 1, 3, 3, 1, 3, 3, 3, 3, 1, 0,
0, 0, 1, 1, 3, 3, 1, 3, 3, 1, 3, 3, 1, 1, 1, 0,
0, 0, 1, 2, 1, 3, 3, 2, 2, 3, 3, 1, 1, 3, 1, 0,
0, 0, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0,
0, 0, 1, 1, 3, 3, 1, 1, 1, 1, 2, 1, 1, 0, 0, 0,
0, 0, 0, 1, 3, 3, 1, 2, 2, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 0, 1, 1, 0, 1, 1, 2, 2, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0
};
static constexpr uint8_t PLAYER_UP_S[] = {
0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0,
0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0,
0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0,
0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 0, 0,
0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 0, 0,
0, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 0,
0, 1, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 1, 0,
0, 0, 1, 1, 3, 3, 1, 1, 1, 1, 3, 3, 1, 1, 0, 0,
0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0,
0, 1, 3, 1, 1, 2, 1, 1, 1, 1, 2, 1, 1, 3, 1, 0,
0, 1, 3, 1, 1, 2, 2, 3, 3, 2, 2, 1, 1, 3, 1, 0,
0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0,
0, 0, 0, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 0, 0, 0,
0, 0, 0, 1, 2, 2, 2, 1, 1, 2, 2, 2, 1, 0, 0, 0,
0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0
};
static constexpr uint8_t PLAYER_UP_0[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0,
0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0,
0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0,
0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 0, 0,
0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 0, 0,
0, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 0,
0, 1, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 1, 0,
0, 1, 1, 1, 3, 3, 1, 1, 1, 1, 3, 3, 1, 1, 0, 0,
0, 1, 3, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0,
0, 0, 1, 1, 1, 2, 1, 1, 1, 1, 2, 1, 3, 3, 1, 0,
0, 0, 0, 1, 1, 2, 2, 3, 3, 2, 2, 1, 3, 3, 1, 0,
0, 0, 0, 0, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 0, 0,
0, 0, 0, 0, 1, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0
};
static constexpr uint8_t PLAYER_UP_1[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0,
0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0,
0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0,
0, 0, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 0, 0,
0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 0, 0,
0, 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, 0,
0, 1, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 1, 0,
0, 0, 1, 1, 3, 3, 1, 1, 1, 1, 3, 3, 1, 1, 1, 0,
0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 3, 1, 0,
0, 1, 3, 3, 1, 2, 1, 1, 1, 1, 2, 1, 1, 1, 0, 0,
0, 1, 3, 3, 1, 2, 2, 3, 3, 2, 2, 1, 1, 0, 0, 0,
0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0
};
static constexpr uint8_t PLAYER_LEFT_0[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0,
0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0,
0, 0, 1, 1, 3, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0,
0, 1, 3, 3, 3, 3, 2, 2, 2, 2, 2, 1, 1, 1, 0, 0,
0, 0, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 0, 0,
0, 0, 0, 1, 3, 1, 3, 3, 1, 1, 1, 1, 1, 1, 0, 0,
0, 0, 0, 1, 3, 1, 3, 3, 1, 3, 3, 1, 1, 0, 0, 0,
0, 0, 0, 1, 3, 3, 3, 3, 3, 3, 3, 1, 0, 0, 0, 0,
0, 0, 0, 0, 1, 2, 3, 3, 3, 1, 1, 2, 1, 0, 0, 0,
0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 2, 1, 0, 0, 0,
0, 0, 0, 1, 1, 1, 1, 1, 1, 3, 3, 1, 1, 0, 0, 0,
0, 0, 1, 2, 2, 1, 2, 2, 1, 3, 3, 1, 2, 1, 0, 0,
0, 0, 0, 1, 2, 2, 1, 1, 1, 1, 1, 2, 2, 1, 0, 0,
0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0
};
static constexpr uint8_t PLAYER_LEFT_1[] = {
0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0,
0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0,
0, 0, 1, 1, 3, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0,
0, 1, 3, 3, 3, 3, 2, 2, 2, 2, 2, 1, 1, 1, 0, 0,
0, 0, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 0, 0,
0, 0, 0, 1, 3, 1, 3, 3, 1, 1, 1, 1, 1, 1, 0, 0,
0, 0, 0, 1, 3, 1, 3, 3, 1, 3, 3, 1, 1, 0, 0, 0,
0, 0, 0, 1, 3, 3, 3, 3, 3, 3, 3, 1, 0, 0, 0, 0,
0, 0, 0, 0, 1, 2, 3, 3, 3, 1, 1, 2, 1, 0, 0, 0,
0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 3, 3, 1, 2, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 3, 3, 1, 2, 1, 0, 0, 0,
0, 0, 0, 0, 0, 1, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0
};
static constexpr uint8_t PLAYER_RIGHT_0[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0,
0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0,
0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 3, 1, 1, 0, 0,
0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 1, 0,
0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 0, 0,
0, 0, 1, 1, 1, 1, 1, 1, 3, 3, 1, 3, 1, 0, 0, 0,
0, 0, 0, 1, 1, 3, 3, 1, 3, 3, 1, 3, 1, 0, 0, 0,
0, 0, 0, 0, 1, 3, 3, 3, 3, 3, 3, 3, 1, 0, 0, 0,
0, 0, 0, 1, 2, 1, 1, 3, 3, 3, 2, 1, 0, 0, 0, 0,
0, 0, 0, 1, 2, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
0, 0, 0, 1, 1, 3, 3, 1, 1, 1, 1, 1, 1, 0, 0, 0,
0, 0, 1, 2, 1, 3, 3, 1, 2, 2, 1, 2, 2, 1, 0, 0,
0, 0, 1, 2, 2, 1, 1, 1, 1, 1, 2, 2, 1, 0, 0, 0,
0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0
};
static constexpr uint8_t PLAYER_RIGHT_1[] = {
0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0, 0,
0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0, 0,
0, 0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 3, 1, 1, 0, 0,
0, 0, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 1, 0,
0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 0, 0,
0, 0, 1, 1, 1, 1, 1, 1, 3, 3, 1, 3, 1, 0, 0, 0,
0, 0, 0, 1, 1, 3, 3, 1, 3, 3, 1, 3, 1, 0, 0, 0,
0, 0, 0, 0, 1, 3, 3, 3, 3, 3, 3, 3, 1, 0, 0, 0,
0, 0, 0, 1, 2, 1, 1, 3, 3, 3, 2, 1, 0, 0, 0, 0,
0, 0, 0, 1, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,
0, 0, 0, 1, 2, 1, 3, 3, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1, 2, 1, 3, 3, 1, 1, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 1, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0
};
static constexpr size_t PLAYER_SPR_X = 16;
static constexpr size_t PLAYER_SPR_Y = 16;
int main(int argc, char* argv[]) {
if (!SDL_Init(SDL_INIT_VIDEO)) {
std::cerr << "Initialization failed: " << SDL_GetError() << "\n";
return -1;
}
size_t playerX = 0;
size_t playerY = 0;
posToCoord(20, &playerX, &playerY);
float player_x_pos = static_cast<float>(playerX);
float player_y_pos = static_cast<float>(playerY);
PathExecutor playerPathMover;
playerPathMover.sqaureSize = static_cast<float>(MYGAME_SQSIZE);
playerPathMover.spriteX = player_x_pos * playerPathMover.sqaureSize;
playerPathMover.spriteY = player_y_pos * playerPathMover.sqaureSize;
playerPathMover.increment = 1.0f; // speed
PathDirect curFacing = PathDirect::Down;
long curStepInDir = -1;
//const float targetY = player_y_pos + (MYGAME_SQSIZE * 4);
SDL_Window* window = nullptr;
SDL_Renderer* renderer = nullptr;
if (!SDL_CreateWindowAndRenderer("The Modern Black Swordsman", SQ_CAM_X_PX, SQ_CAM_Y_PX, 0, &window, &renderer)) {
std::cerr << "Failed to create window/renderer: " << SDL_GetError() << "\n";
return -1;
}
SDL_Texture* texture1 = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_INDEX8, SDL_TEXTUREACCESS_STATIC, 16, 16);
SDL_SetTextureBlendMode(texture1, SDL_BLENDMODE_BLEND);
SDL_Texture* texture2 = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_INDEX8, SDL_TEXTUREACCESS_STATIC, 16, 16);
SDL_SetTextureBlendMode(texture2, SDL_BLENDMODE_BLEND);
SDL_Texture* texture3 = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_INDEX8, SDL_TEXTUREACCESS_STATIC, 16, 16);
SDL_SetTextureBlendMode(texture3, SDL_BLENDMODE_BLEND);
SDL_Texture* texture4 = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_INDEX8, SDL_TEXTUREACCESS_STATIC, 16, 16);
SDL_SetTextureBlendMode(texture4, SDL_BLENDMODE_BLEND);
SDL_Texture* texture5 = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_INDEX8, SDL_TEXTUREACCESS_STATIC, 16, 16);
SDL_SetTextureBlendMode(texture5, SDL_BLENDMODE_BLEND);
SDL_Texture* texture6 = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_INDEX8, SDL_TEXTUREACCESS_STATIC, 16, 16);
SDL_SetTextureBlendMode(texture6, SDL_BLENDMODE_BLEND);
SDL_Texture* texture7 = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_INDEX8, SDL_TEXTUREACCESS_STATIC, 16, 16);
SDL_SetTextureBlendMode(texture7, SDL_BLENDMODE_BLEND);
SDL_Texture* texture8 = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_INDEX8, SDL_TEXTUREACCESS_STATIC, 16, 16);
SDL_SetTextureBlendMode(texture8, SDL_BLENDMODE_BLEND);
SDL_Texture* texture9 = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_INDEX8, SDL_TEXTUREACCESS_STATIC, 16, 16);
SDL_SetTextureBlendMode(texture9, SDL_BLENDMODE_BLEND);
SDL_Texture* texture10 = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_INDEX8, SDL_TEXTUREACCESS_STATIC, 16, 16);
SDL_SetTextureBlendMode(texture10, SDL_BLENDMODE_BLEND);
SDL_Palette* spritePalette = SDL_CreatePalette(4);
SDL_SetPaletteColors(spritePalette, PLAYER_PALT, 0, 4);
SDL_SetTexturePalette(texture1, spritePalette);
SDL_UpdateTexture(texture1, nullptr, PLAYER_DOWN_S, 16);
SDL_SetTexturePalette(texture2, spritePalette);
SDL_UpdateTexture(texture2, nullptr, PLAYER_DOWN_0, 16);
SDL_SetTexturePalette(texture3, spritePalette);
SDL_UpdateTexture(texture3, nullptr, PLAYER_DOWN_1, 16);
SDL_SetTexturePalette(texture4, spritePalette);
SDL_UpdateTexture(texture4, nullptr, PLAYER_UP_S, 16);
SDL_SetTexturePalette(texture5, spritePalette);
SDL_UpdateTexture(texture5, nullptr, PLAYER_UP_0, 16);
SDL_SetTexturePalette(texture6, spritePalette);
SDL_UpdateTexture(texture6, nullptr, PLAYER_UP_1, 16);
SDL_SetTexturePalette(texture7, spritePalette);
SDL_UpdateTexture(texture7, nullptr, PLAYER_LEFT_0, 16);
SDL_SetTexturePalette(texture8, spritePalette);
SDL_UpdateTexture(texture8, nullptr, PLAYER_LEFT_1, 16);
SDL_SetTexturePalette(texture9, spritePalette);
SDL_UpdateTexture(texture9, nullptr, PLAYER_RIGHT_0, 16);
SDL_SetTexturePalette(texture10, spritePalette);
SDL_UpdateTexture(texture10, nullptr, PLAYER_RIGHT_1, 16);
bool running = true;
// Fixed timestep setup (60 updates per second)
const Uint64 FIXED_DELTA_TIME_NS = 1000000000 / 60;
Uint64 current_time_ns = SDL_GetTicksNS();
Uint64 previous_time_ns = current_time_ns;
Uint64 accumulator_ns = 0;
// Inventory Math Variables
const float INV_X = 550.0f;
const float INV_Y = 150.0f;
const float SLOT_SIZE = 40.0f;
const float SLOT_PADDING = 6.0f;
const int COLS = 4;
const int ROWS = 7;
std::string curPathForPlayer;
while (running) {
current_time_ns = SDL_GetTicksNS();
Uint64 elapsed_ns = current_time_ns - previous_time_ns;
previous_time_ns = current_time_ns;
if (elapsed_ns > 250000000) elapsed_ns = 250000000;
accumulator_ns += elapsed_ns;
// ==========================================
// 1. INPUT DETECTION (OS Events & Mouse)
// ==========================================
SDL_Event event;
while (SDL_PollEvent(&event)) {
if (event.type == SDL_EVENT_QUIT) {
running = false;
}
// Detect Mouse Clicks in SDL3
else if (event.type == SDL_EVENT_MOUSE_BUTTON_DOWN) {
if (event.button.button == SDL_BUTTON_LEFT) {
// SDL3 provides mouse coordinates as clean floats
float mousepre_x = event.button.x;
float mousepre_y = event.button.y;
float mouse_x = 0.0f;
float mouse_y = 0.0f;
canvasWarp(playerPathMover.spriteX, playerPathMover.spriteY, SQ_CAM_X_PXF, SQ_CAM_Y_PXF, SQ_MAX_X_PXF, SQ_MAX_Y_PXF, mousepre_x, mousepre_y, &mouse_x, &mouse_y);
int groundRes = getGameBoardSpot(mouse_x, mouse_y);
std::cout << "Clicked ground: " << getGameBoardSpot(mouse_x, mouse_y) << "\n";
const int mousexnum = static_cast<int>(mouse_x) / MYGAME_SQSIZE;
const int mouseynum = static_cast<int>(mouse_y) / MYGAME_SQSIZE;
const int curPlayerX = static_cast<int>(playerPathMover.spriteX) / MYGAME_SQSIZE;
const int curPlayerY = static_cast<int>(playerPathMover.spriteY) / MYGAME_SQSIZE;
std::printf("mousexnum=%d mouseynum=%d curPlayerX=%d curPlayerY=%d\n", mousexnum, mouseynum, curPlayerX, curPlayerY);
if (groundRes != 1 && playerPathMover.curPath == nullptr) {
curPathForPlayer = PathFinder::createPath(GAMEBOARD, SQ_MAX_X, SQ_MAX_Y, curPlayerX, curPlayerY, mousexnum, mouseynum);
playerPathMover.curPath = curPathForPlayer.c_str();
playerPathMover.begin();
}
}
}
}
// ==========================================
// 2. FIXED PHYSICS / LOGIC TIMESTEP
// ==========================================
while (accumulator_ns >= FIXED_DELTA_TIME_NS) {
// Update item animations, physics, and skilling timers here
playerPathMover.moveSprite();
// based on path, change target.
accumulator_ns -= FIXED_DELTA_TIME_NS;
}
// ==========================================
// 3. PAINT / RENDERING THE WINDOW
// ==========================================
// A. Wipe the canvas clean with background color (Dark Berserk Charcoal)
SDL_SetRenderDrawColor(renderer, 20, 20, 20, 255);
SDL_RenderClear(renderer);
for (size_t i = 0; i < SQ_TOTAL_CNT; ++i)
{
const int groundCode = GAMEBOARD[i];
size_t xnum = 0;
size_t ynum = 0;
posToCoord(i, &xnum, &ynum);
float curGroundX = 0.0f;
float curGroundY = 0.0f;
canvasTranslate(playerPathMover.spriteX, playerPathMover.spriteY, SQ_CAM_X_PXF, SQ_CAM_Y_PXF, SQ_MAX_X_PXF, SQ_MAX_Y_PXF, static_cast<float>(xnum * MYGAME_SQSIZE), static_cast<float>(ynum * MYGAME_SQSIZE), &curGroundX, &curGroundY);
SDL_FRect pecrect = { curGroundX, curGroundY, MYGAME_SQSIZE, MYGAME_SQSIZE };
if (groundCode == 0) {
SDL_SetRenderDrawColor(renderer, GRASSCOLOR.r, GRASSCOLOR.g, GRASSCOLOR.b, 255);
} else if (groundCode == 1) {
SDL_SetRenderDrawColor(renderer, ROCKCOLOR.r, ROCKCOLOR.g, ROCKCOLOR.b, 255);
}
// todo consider above zero check
if (curGroundX < 0 || curGroundY < 0)
continue;
SDL_RenderFillRect(renderer, &pecrect);
}
float playerScreenX = 0.0f;
float playerScreenY = 0.0f;
canvasTranslate(playerPathMover.spriteX, playerPathMover.spriteY,
SQ_CAM_X_PXF, SQ_CAM_Y_PXF,
SQ_MAX_X_PXF, SQ_MAX_Y_PXF,
playerPathMover.spriteX, playerPathMover.spriteY,
&playerScreenX, &playerScreenY);
SDL_FRect dst_rect = { playerScreenX, playerScreenY, MYGAME_SQSIZE, MYGAME_SQSIZE };
SDL_Texture* chosen = nullptr;
float moveDist = 0.0f;
switch (playerPathMover.lastPath) {
case PathDirect::Down:
moveDist = playerPathMover.targetY - playerPathMover.spriteY;
if (moveDist > 0.0f) {
if (moveDist > 8.0f) {
chosen = texture2;
} else {
chosen = texture3;
}
} else {
chosen = texture1;
}
break;
case PathDirect::Up:
moveDist = playerPathMover.spriteY - playerPathMover.targetY;
if (moveDist > 0.0f) {
if (moveDist > 8.0f) {
chosen = texture5;
} else {
chosen = texture6;
}
} else {
chosen = texture4;
}
break;
case PathDirect::Left:
moveDist = playerPathMover.spriteX - playerPathMover.targetX;
if (moveDist > 0.0f) {
if (moveDist > 8.0f) {
chosen = texture7;
} else {
chosen = texture8;
}
} else {
chosen = texture8;
}
break;
case PathDirect::Right:
moveDist = playerPathMover.targetX - playerPathMover.spriteX;
if (moveDist > 0.0f) {
if (moveDist > 8.0f) {
chosen = texture9;
} else {
chosen = texture10;
}
} else {
chosen = texture10;
}
break;
}
SDL_RenderTexture(renderer, chosen, nullptr, &dst_rect);
//SDL_RenderFillRect(renderer, &specrect);
// C. Command the GPU to push the backbuffer into the visible monitor frame
SDL_RenderPresent(renderer);
}
SDL_DestroyRenderer(renderer);
SDL_DestroyPalette(spritePalette);
SDL_DestroyTexture(texture1);
SDL_DestroyTexture(texture2);
SDL_DestroyTexture(texture3);
SDL_DestroyTexture(texture4);
SDL_DestroyTexture(texture5);
SDL_DestroyTexture(texture6);
SDL_DestroyTexture(texture7);
SDL_DestroyTexture(texture8);
SDL_DestroyTexture(texture9);
SDL_DestroyTexture(texture10);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment