mirror of
https://github.com/libsdl-org/SDL.git
synced 2025-06-07 16:01:10 +00:00
examples: Smush game/01-snake into one source file, clean up some things.
This commit is contained in:
parent
906044569d
commit
f6fc5e2881
4 changed files with 202 additions and 231 deletions
|
@ -188,7 +188,7 @@ add_sdl_example_executable(audio-simple-playback-callback SOURCES audio/02-simpl
|
||||||
add_sdl_example_executable(audio-load-wav SOURCES audio/03-load-wav/load-wav.c DATAFILES ${CMAKE_CURRENT_SOURCE_DIR}/../test/sample.wav)
|
add_sdl_example_executable(audio-load-wav SOURCES audio/03-load-wav/load-wav.c DATAFILES ${CMAKE_CURRENT_SOURCE_DIR}/../test/sample.wav)
|
||||||
add_sdl_example_executable(camera-read-and-draw SOURCES camera/01-read-and-draw/read-and-draw.c)
|
add_sdl_example_executable(camera-read-and-draw SOURCES camera/01-read-and-draw/read-and-draw.c)
|
||||||
add_sdl_example_executable(pen-drawing-lines SOURCES pen/01-drawing-lines/drawing-lines.c)
|
add_sdl_example_executable(pen-drawing-lines SOURCES pen/01-drawing-lines/drawing-lines.c)
|
||||||
add_sdl_example_executable(game-snake SOURCES game/01-snake/main.c game/01-snake/snake.c)
|
add_sdl_example_executable(game-snake SOURCES game/01-snake/snake.c)
|
||||||
|
|
||||||
|
|
||||||
if(PSP)
|
if(PSP)
|
||||||
|
|
|
@ -1,155 +0,0 @@
|
||||||
/*
|
|
||||||
* This example code implements a Snake game that showcases some of the
|
|
||||||
* functionalities of SDL, such as timer callbacks and event handling.
|
|
||||||
*
|
|
||||||
* This code is public domain. Feel free to use it for any purpose!
|
|
||||||
*/
|
|
||||||
#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */
|
|
||||||
#include <SDL3/SDL.h>
|
|
||||||
#include <SDL3/SDL_main.h>
|
|
||||||
#include <stdlib.h> /* malloc(), free() */
|
|
||||||
|
|
||||||
#include "snake.h"
|
|
||||||
|
|
||||||
#define STEP_RATE_IN_MILLISECONDS 125
|
|
||||||
#define SNAKE_BLOCK_SIZE_IN_PIXELS 24
|
|
||||||
#define SDL_WINDOW_WIDTH (SNAKE_BLOCK_SIZE_IN_PIXELS * SNAKE_GAME_WIDTH)
|
|
||||||
#define SDL_WINDOW_HEIGHT (SNAKE_BLOCK_SIZE_IN_PIXELS * SNAKE_GAME_HEIGHT)
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
SDL_Window *window;
|
|
||||||
SDL_Renderer *renderer;
|
|
||||||
SDL_TimerID step_timer;
|
|
||||||
SnakeContext snake_ctx;
|
|
||||||
} AppState;
|
|
||||||
|
|
||||||
static Uint32 sdl_timer_callback_(void *payload, SDL_TimerID timer_id, Uint32 interval)
|
|
||||||
{
|
|
||||||
/* NOTE: snake_step is not called here directly for multithreaded concerns. */
|
|
||||||
SDL_Event event;
|
|
||||||
SDL_zero(event);
|
|
||||||
event.type = SDL_EVENT_USER;
|
|
||||||
SDL_PushEvent(&event);
|
|
||||||
return interval;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int handle_key_event_(SnakeContext *ctx, SDL_Scancode key_code)
|
|
||||||
{
|
|
||||||
switch (key_code) {
|
|
||||||
/* Quit. */
|
|
||||||
case SDL_SCANCODE_ESCAPE:
|
|
||||||
case SDL_SCANCODE_Q:
|
|
||||||
return SDL_APP_SUCCESS;
|
|
||||||
/* Restart the game as if the program was launched. */
|
|
||||||
case SDL_SCANCODE_R:
|
|
||||||
snake_initialize(ctx, SDL_rand);
|
|
||||||
break;
|
|
||||||
/* Decide new direction of the snake. */
|
|
||||||
case SDL_SCANCODE_RIGHT:
|
|
||||||
snake_redir(ctx, SNAKE_DIR_RIGHT);
|
|
||||||
break;
|
|
||||||
case SDL_SCANCODE_UP:
|
|
||||||
snake_redir(ctx, SNAKE_DIR_UP);
|
|
||||||
break;
|
|
||||||
case SDL_SCANCODE_LEFT:
|
|
||||||
snake_redir(ctx, SNAKE_DIR_LEFT);
|
|
||||||
break;
|
|
||||||
case SDL_SCANCODE_DOWN:
|
|
||||||
snake_redir(ctx, SNAKE_DIR_DOWN);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return SDL_APP_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void set_rect_xy_(SDL_FRect *r, short x, short y)
|
|
||||||
{
|
|
||||||
r->x = (float)(x * SNAKE_BLOCK_SIZE_IN_PIXELS);
|
|
||||||
r->y = (float)(y * SNAKE_BLOCK_SIZE_IN_PIXELS);
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_AppResult SDL_AppIterate(void *appstate)
|
|
||||||
{
|
|
||||||
AppState *as;
|
|
||||||
SnakeContext *ctx;
|
|
||||||
SDL_FRect r;
|
|
||||||
unsigned i;
|
|
||||||
unsigned j;
|
|
||||||
int ct;
|
|
||||||
as = (AppState *)appstate;
|
|
||||||
ctx = &as->snake_ctx;
|
|
||||||
r.w = r.h = SNAKE_BLOCK_SIZE_IN_PIXELS;
|
|
||||||
SDL_SetRenderDrawColor(as->renderer, 0, 0, 0, 255);
|
|
||||||
SDL_RenderClear(as->renderer);
|
|
||||||
for (i = 0; i < SNAKE_GAME_WIDTH; i++) {
|
|
||||||
for (j = 0; j < SNAKE_GAME_HEIGHT; j++) {
|
|
||||||
ct = snake_cell_at(ctx, i, j);
|
|
||||||
if (ct == SNAKE_CELL_NOTHING)
|
|
||||||
continue;
|
|
||||||
set_rect_xy_(&r, i, j);
|
|
||||||
if (ct == SNAKE_CELL_FOOD)
|
|
||||||
SDL_SetRenderDrawColor(as->renderer, 0, 0, 128, 255);
|
|
||||||
else /* body */
|
|
||||||
SDL_SetRenderDrawColor(as->renderer, 0, 128, 0, 255);
|
|
||||||
SDL_RenderFillRect(as->renderer, &r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
SDL_SetRenderDrawColor(as->renderer, 255, 255, 0, 255); /*head*/
|
|
||||||
set_rect_xy_(&r, ctx->head_xpos, ctx->head_ypos);
|
|
||||||
SDL_RenderFillRect(as->renderer, &r);
|
|
||||||
SDL_RenderPresent(as->renderer);
|
|
||||||
return SDL_APP_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
|
|
||||||
{
|
|
||||||
(void)argc;
|
|
||||||
(void)argv;
|
|
||||||
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER)) {
|
|
||||||
return SDL_APP_FAILURE;
|
|
||||||
}
|
|
||||||
SDL_SetHint("SDL_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR", "0");
|
|
||||||
AppState *as = malloc(sizeof(AppState));
|
|
||||||
*appstate = as;
|
|
||||||
as->step_timer = 0;
|
|
||||||
if (!SDL_CreateWindowAndRenderer("examples/game/snake", SDL_WINDOW_WIDTH, SDL_WINDOW_HEIGHT, 0, &as->window, &as->renderer)) {
|
|
||||||
return SDL_APP_FAILURE;
|
|
||||||
}
|
|
||||||
snake_initialize(&as->snake_ctx, SDL_rand);
|
|
||||||
as->step_timer = SDL_AddTimer(
|
|
||||||
STEP_RATE_IN_MILLISECONDS,
|
|
||||||
sdl_timer_callback_,
|
|
||||||
NULL);
|
|
||||||
if (as->step_timer == 0) {
|
|
||||||
return SDL_APP_FAILURE;
|
|
||||||
}
|
|
||||||
return SDL_APP_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_AppResult SDL_AppEvent(void *appstate, const SDL_Event *event)
|
|
||||||
{
|
|
||||||
SnakeContext *ctx = &((AppState *)appstate)->snake_ctx;
|
|
||||||
switch (event->type) {
|
|
||||||
case SDL_EVENT_QUIT:
|
|
||||||
return SDL_APP_SUCCESS;
|
|
||||||
case SDL_EVENT_USER:
|
|
||||||
snake_step(ctx, SDL_rand);
|
|
||||||
break;
|
|
||||||
case SDL_EVENT_KEY_DOWN:
|
|
||||||
return handle_key_event_(ctx, event->key.scancode);
|
|
||||||
}
|
|
||||||
return SDL_APP_CONTINUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SDL_AppQuit(void *appstate)
|
|
||||||
{
|
|
||||||
if (appstate != NULL) {
|
|
||||||
AppState *as = (AppState *)appstate;
|
|
||||||
SDL_RemoveTimer(as->step_timer);
|
|
||||||
SDL_DestroyRenderer(as->renderer);
|
|
||||||
SDL_DestroyWindow(as->window);
|
|
||||||
free(as);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,24 +4,89 @@
|
||||||
*
|
*
|
||||||
* This code is public domain. Feel free to use it for any purpose!
|
* This code is public domain. Feel free to use it for any purpose!
|
||||||
*/
|
*/
|
||||||
#include "snake.h"
|
|
||||||
|
|
||||||
#include <limits.h> /* CHAR_BIT, CHAR_MAX */
|
#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */
|
||||||
#include <string.h> /* memcpy() */
|
#include <SDL3/SDL.h>
|
||||||
|
#include <SDL3/SDL_main.h>
|
||||||
|
|
||||||
|
#define STEP_RATE_IN_MILLISECONDS 125
|
||||||
|
#define SNAKE_BLOCK_SIZE_IN_PIXELS 24
|
||||||
|
#define SDL_WINDOW_WIDTH (SNAKE_BLOCK_SIZE_IN_PIXELS * SNAKE_GAME_WIDTH)
|
||||||
|
#define SDL_WINDOW_HEIGHT (SNAKE_BLOCK_SIZE_IN_PIXELS * SNAKE_GAME_HEIGHT)
|
||||||
|
|
||||||
|
#define SNAKE_GAME_WIDTH 24U
|
||||||
|
#define SNAKE_GAME_HEIGHT 18U
|
||||||
|
#define SNAKE_MATRIX_SIZE (SNAKE_GAME_WIDTH * SNAKE_GAME_HEIGHT)
|
||||||
|
|
||||||
#define THREE_BITS 0x7U /* ~CHAR_MAX >> (CHAR_BIT - SNAKE_CELL_MAX_BITS) */
|
#define THREE_BITS 0x7U /* ~CHAR_MAX >> (CHAR_BIT - SNAKE_CELL_MAX_BITS) */
|
||||||
#define SHIFT(x, y) (((x) + ((y) * SNAKE_GAME_WIDTH)) * SNAKE_CELL_MAX_BITS)
|
#define SHIFT(x, y) (((x) + ((y) * SNAKE_GAME_WIDTH)) * SNAKE_CELL_MAX_BITS)
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
SNAKE_CELL_NOTHING = 0U,
|
||||||
|
SNAKE_CELL_SRIGHT = 1U,
|
||||||
|
SNAKE_CELL_SUP = 2U,
|
||||||
|
SNAKE_CELL_SLEFT = 3U,
|
||||||
|
SNAKE_CELL_SDOWN = 4U,
|
||||||
|
SNAKE_CELL_FOOD = 5U
|
||||||
|
} SnakeCell;
|
||||||
|
|
||||||
|
#define SNAKE_CELL_MAX_BITS 3U /* floor(log2(SNAKE_CELL_FOOD)) + 1 */
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
SNAKE_DIR_RIGHT,
|
||||||
|
SNAKE_DIR_UP,
|
||||||
|
SNAKE_DIR_LEFT,
|
||||||
|
SNAKE_DIR_DOWN
|
||||||
|
} SnakeDirection;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
unsigned char cells[(SNAKE_MATRIX_SIZE * SNAKE_CELL_MAX_BITS) / 8U];
|
||||||
|
char head_xpos;
|
||||||
|
char head_ypos;
|
||||||
|
char tail_xpos;
|
||||||
|
char tail_ypos;
|
||||||
|
char next_dir;
|
||||||
|
char inhibit_tail_step;
|
||||||
|
unsigned occupied_cells;
|
||||||
|
} SnakeContext;
|
||||||
|
|
||||||
|
typedef Sint32 (SDLCALL *RandFunc)(Sint32 n);
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
SDL_Window *window;
|
||||||
|
SDL_Renderer *renderer;
|
||||||
|
SDL_TimerID step_timer;
|
||||||
|
SnakeContext snake_ctx;
|
||||||
|
} AppState;
|
||||||
|
|
||||||
|
SnakeCell snake_cell_at(const SnakeContext *ctx, char x, char y)
|
||||||
|
{
|
||||||
|
const int shift = SHIFT(x, y);
|
||||||
|
unsigned short range;
|
||||||
|
SDL_memcpy(&range, ctx->cells + (shift / 8), sizeof(range));
|
||||||
|
return (SnakeCell)((range >> (shift % 8)) & THREE_BITS);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void set_rect_xy_(SDL_FRect *r, short x, short y)
|
||||||
|
{
|
||||||
|
r->x = (float)(x * SNAKE_BLOCK_SIZE_IN_PIXELS);
|
||||||
|
r->y = (float)(y * SNAKE_BLOCK_SIZE_IN_PIXELS);
|
||||||
|
}
|
||||||
|
|
||||||
static void put_cell_at_(SnakeContext *ctx, char x, char y, SnakeCell ct)
|
static void put_cell_at_(SnakeContext *ctx, char x, char y, SnakeCell ct)
|
||||||
{
|
{
|
||||||
const int shift = SHIFT(x, y);
|
const int shift = SHIFT(x, y);
|
||||||
const int adjust = shift % CHAR_BIT;
|
const int adjust = shift % 8;
|
||||||
unsigned char *const pos = ctx->cells + (shift / CHAR_BIT);
|
unsigned char *const pos = ctx->cells + (shift / 8);
|
||||||
unsigned short range;
|
unsigned short range;
|
||||||
memcpy(&range, pos, sizeof(range));
|
SDL_memcpy(&range, pos, sizeof(range));
|
||||||
range &= ~(THREE_BITS << adjust); /* clear bits */
|
range &= ~(THREE_BITS << adjust); /* clear bits */
|
||||||
range |= (ct & THREE_BITS) << adjust;
|
range |= (ct & THREE_BITS) << adjust;
|
||||||
memcpy(pos, &range, sizeof(range));
|
SDL_memcpy(pos, &range, sizeof(range));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int are_cells_full_(SnakeContext *ctx)
|
static int are_cells_full_(SnakeContext *ctx)
|
||||||
|
@ -31,11 +96,9 @@ static int are_cells_full_(SnakeContext *ctx)
|
||||||
|
|
||||||
static void new_food_pos_(SnakeContext *ctx, RandFunc rand)
|
static void new_food_pos_(SnakeContext *ctx, RandFunc rand)
|
||||||
{
|
{
|
||||||
char x;
|
while (SDL_TRUE) {
|
||||||
char y;
|
const char x = (char) rand(SNAKE_GAME_WIDTH);
|
||||||
for (;;) {
|
const char y = (char) rand(SNAKE_GAME_HEIGHT);
|
||||||
x = (char) rand(SNAKE_GAME_WIDTH);
|
|
||||||
y = (char) rand(SNAKE_GAME_HEIGHT);
|
|
||||||
if (snake_cell_at(ctx, x, y) == SNAKE_CELL_NOTHING) {
|
if (snake_cell_at(ctx, x, y) == SNAKE_CELL_NOTHING) {
|
||||||
put_cell_at_(ctx, x, y, SNAKE_CELL_FOOD);
|
put_cell_at_(ctx, x, y, SNAKE_CELL_FOOD);
|
||||||
break;
|
break;
|
||||||
|
@ -46,7 +109,7 @@ static void new_food_pos_(SnakeContext *ctx, RandFunc rand)
|
||||||
void snake_initialize(SnakeContext *ctx, RandFunc rand)
|
void snake_initialize(SnakeContext *ctx, RandFunc rand)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
memset(ctx, 0, sizeof ctx->cells);
|
SDL_zeroa(ctx->cells);
|
||||||
ctx->head_xpos = ctx->tail_xpos = SNAKE_GAME_WIDTH / 2;
|
ctx->head_xpos = ctx->tail_xpos = SNAKE_GAME_WIDTH / 2;
|
||||||
ctx->head_ypos = ctx->tail_ypos = SNAKE_GAME_HEIGHT / 2;
|
ctx->head_ypos = ctx->tail_ypos = SNAKE_GAME_HEIGHT / 2;
|
||||||
ctx->next_dir = SNAKE_DIR_RIGHT;
|
ctx->next_dir = SNAKE_DIR_RIGHT;
|
||||||
|
@ -65,17 +128,19 @@ void snake_redir(SnakeContext *ctx, SnakeDirection dir)
|
||||||
if ((dir == SNAKE_DIR_RIGHT && ct != SNAKE_CELL_SLEFT) ||
|
if ((dir == SNAKE_DIR_RIGHT && ct != SNAKE_CELL_SLEFT) ||
|
||||||
(dir == SNAKE_DIR_UP && ct != SNAKE_CELL_SDOWN) ||
|
(dir == SNAKE_DIR_UP && ct != SNAKE_CELL_SDOWN) ||
|
||||||
(dir == SNAKE_DIR_LEFT && ct != SNAKE_CELL_SRIGHT) ||
|
(dir == SNAKE_DIR_LEFT && ct != SNAKE_CELL_SRIGHT) ||
|
||||||
(dir == SNAKE_DIR_DOWN && ct != SNAKE_CELL_SUP))
|
(dir == SNAKE_DIR_DOWN && ct != SNAKE_CELL_SUP)) {
|
||||||
ctx->next_dir = dir;
|
ctx->next_dir = dir;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void wrap_around_(char *val, char max)
|
static void wrap_around_(char *val, char max)
|
||||||
{
|
{
|
||||||
if (*val < 0)
|
if (*val < 0) {
|
||||||
*val = max - 1;
|
*val = max - 1;
|
||||||
if (*val > max - 1)
|
} else if (*val > max - 1) {
|
||||||
*val = 0;
|
*val = 0;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void snake_step(SnakeContext *ctx, RandFunc rand)
|
void snake_step(SnakeContext *ctx, RandFunc rand)
|
||||||
{
|
{
|
||||||
|
@ -145,10 +210,125 @@ void snake_step(SnakeContext *ctx, RandFunc rand)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SnakeCell snake_cell_at(const SnakeContext *ctx, char x, char y)
|
static Uint32 sdl_timer_callback_(void *payload, SDL_TimerID timer_id, Uint32 interval)
|
||||||
{
|
{
|
||||||
const int shift = SHIFT(x, y);
|
/* NOTE: snake_step is not called here directly for multithreaded concerns. */
|
||||||
unsigned short range;
|
SDL_Event event;
|
||||||
memcpy(&range, ctx->cells + (shift / CHAR_BIT), sizeof(range));
|
SDL_zero(event);
|
||||||
return (SnakeCell)((range >> (shift % CHAR_BIT)) & THREE_BITS);
|
event.type = SDL_EVENT_USER;
|
||||||
|
SDL_PushEvent(&event);
|
||||||
|
return interval;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int handle_key_event_(SnakeContext *ctx, SDL_Scancode key_code)
|
||||||
|
{
|
||||||
|
switch (key_code) {
|
||||||
|
/* Quit. */
|
||||||
|
case SDL_SCANCODE_ESCAPE:
|
||||||
|
case SDL_SCANCODE_Q:
|
||||||
|
return SDL_APP_SUCCESS;
|
||||||
|
/* Restart the game as if the program was launched. */
|
||||||
|
case SDL_SCANCODE_R:
|
||||||
|
snake_initialize(ctx, SDL_rand);
|
||||||
|
break;
|
||||||
|
/* Decide new direction of the snake. */
|
||||||
|
case SDL_SCANCODE_RIGHT:
|
||||||
|
snake_redir(ctx, SNAKE_DIR_RIGHT);
|
||||||
|
break;
|
||||||
|
case SDL_SCANCODE_UP:
|
||||||
|
snake_redir(ctx, SNAKE_DIR_UP);
|
||||||
|
break;
|
||||||
|
case SDL_SCANCODE_LEFT:
|
||||||
|
snake_redir(ctx, SNAKE_DIR_LEFT);
|
||||||
|
break;
|
||||||
|
case SDL_SCANCODE_DOWN:
|
||||||
|
snake_redir(ctx, SNAKE_DIR_DOWN);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return SDL_APP_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_AppResult SDL_AppIterate(void *appstate)
|
||||||
|
{
|
||||||
|
AppState *as;
|
||||||
|
SnakeContext *ctx;
|
||||||
|
SDL_FRect r;
|
||||||
|
unsigned i;
|
||||||
|
unsigned j;
|
||||||
|
int ct;
|
||||||
|
as = (AppState *)appstate;
|
||||||
|
ctx = &as->snake_ctx;
|
||||||
|
r.w = r.h = SNAKE_BLOCK_SIZE_IN_PIXELS;
|
||||||
|
SDL_SetRenderDrawColor(as->renderer, 0, 0, 0, 255);
|
||||||
|
SDL_RenderClear(as->renderer);
|
||||||
|
for (i = 0; i < SNAKE_GAME_WIDTH; i++) {
|
||||||
|
for (j = 0; j < SNAKE_GAME_HEIGHT; j++) {
|
||||||
|
ct = snake_cell_at(ctx, i, j);
|
||||||
|
if (ct == SNAKE_CELL_NOTHING)
|
||||||
|
continue;
|
||||||
|
set_rect_xy_(&r, i, j);
|
||||||
|
if (ct == SNAKE_CELL_FOOD)
|
||||||
|
SDL_SetRenderDrawColor(as->renderer, 0, 0, 128, 255);
|
||||||
|
else /* body */
|
||||||
|
SDL_SetRenderDrawColor(as->renderer, 0, 128, 0, 255);
|
||||||
|
SDL_RenderFillRect(as->renderer, &r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SDL_SetRenderDrawColor(as->renderer, 255, 255, 0, 255); /*head*/
|
||||||
|
set_rect_xy_(&r, ctx->head_xpos, ctx->head_ypos);
|
||||||
|
SDL_RenderFillRect(as->renderer, &r);
|
||||||
|
SDL_RenderPresent(as->renderer);
|
||||||
|
return SDL_APP_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_AppResult SDL_AppInit(void **appstate, int argc, char *argv[])
|
||||||
|
{
|
||||||
|
if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER)) {
|
||||||
|
return SDL_APP_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
AppState *as = SDL_calloc(1, sizeof(AppState));
|
||||||
|
|
||||||
|
*appstate = as;
|
||||||
|
|
||||||
|
if (!SDL_CreateWindowAndRenderer("examples/game/snake", SDL_WINDOW_WIDTH, SDL_WINDOW_HEIGHT, 0, &as->window, &as->renderer)) {
|
||||||
|
return SDL_APP_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
snake_initialize(&as->snake_ctx, SDL_rand);
|
||||||
|
|
||||||
|
as->step_timer = SDL_AddTimer(STEP_RATE_IN_MILLISECONDS, sdl_timer_callback_, NULL);
|
||||||
|
if (as->step_timer == 0) {
|
||||||
|
return SDL_APP_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SDL_APP_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_AppResult SDL_AppEvent(void *appstate, const SDL_Event *event)
|
||||||
|
{
|
||||||
|
SnakeContext *ctx = &((AppState *)appstate)->snake_ctx;
|
||||||
|
switch (event->type) {
|
||||||
|
case SDL_EVENT_QUIT:
|
||||||
|
return SDL_APP_SUCCESS;
|
||||||
|
case SDL_EVENT_USER:
|
||||||
|
snake_step(ctx, SDL_rand);
|
||||||
|
break;
|
||||||
|
case SDL_EVENT_KEY_DOWN:
|
||||||
|
return handle_key_event_(ctx, event->key.scancode);
|
||||||
|
}
|
||||||
|
return SDL_APP_CONTINUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDL_AppQuit(void *appstate)
|
||||||
|
{
|
||||||
|
if (appstate != NULL) {
|
||||||
|
AppState *as = (AppState *)appstate;
|
||||||
|
SDL_RemoveTimer(as->step_timer);
|
||||||
|
SDL_DestroyRenderer(as->renderer);
|
||||||
|
SDL_DestroyWindow(as->window);
|
||||||
|
SDL_free(as);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,54 +0,0 @@
|
||||||
/*
|
|
||||||
* Interface definition of the Snake game.
|
|
||||||
*
|
|
||||||
* This code is public domain. Feel free to use it for any purpose!
|
|
||||||
*/
|
|
||||||
#ifndef SNAKE_H
|
|
||||||
#define SNAKE_H
|
|
||||||
|
|
||||||
#include <SDL3/SDL.h>
|
|
||||||
|
|
||||||
#define SNAKE_GAME_WIDTH 24U
|
|
||||||
#define SNAKE_GAME_HEIGHT 18U
|
|
||||||
#define SNAKE_MATRIX_SIZE (SNAKE_GAME_WIDTH * SNAKE_GAME_HEIGHT)
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
SNAKE_CELL_NOTHING = 0U,
|
|
||||||
SNAKE_CELL_SRIGHT = 1U,
|
|
||||||
SNAKE_CELL_SUP = 2U,
|
|
||||||
SNAKE_CELL_SLEFT = 3U,
|
|
||||||
SNAKE_CELL_SDOWN = 4U,
|
|
||||||
SNAKE_CELL_FOOD = 5U
|
|
||||||
} SnakeCell;
|
|
||||||
|
|
||||||
#define SNAKE_CELL_MAX_BITS 3U /* floor(log2(SNAKE_CELL_FOOD)) + 1 */
|
|
||||||
|
|
||||||
typedef enum
|
|
||||||
{
|
|
||||||
SNAKE_DIR_RIGHT,
|
|
||||||
SNAKE_DIR_UP,
|
|
||||||
SNAKE_DIR_LEFT,
|
|
||||||
SNAKE_DIR_DOWN
|
|
||||||
} SnakeDirection;
|
|
||||||
|
|
||||||
typedef struct
|
|
||||||
{
|
|
||||||
unsigned char cells[(SNAKE_MATRIX_SIZE * SNAKE_CELL_MAX_BITS) / 8U];
|
|
||||||
char head_xpos;
|
|
||||||
char head_ypos;
|
|
||||||
char tail_xpos;
|
|
||||||
char tail_ypos;
|
|
||||||
char next_dir;
|
|
||||||
char inhibit_tail_step;
|
|
||||||
unsigned occupied_cells;
|
|
||||||
} SnakeContext;
|
|
||||||
|
|
||||||
typedef Sint32 (SDLCALL *RandFunc)(Sint32 n);
|
|
||||||
|
|
||||||
void snake_initialize(SnakeContext *ctx, RandFunc rand);
|
|
||||||
void snake_redir(SnakeContext *ctx, SnakeDirection dir);
|
|
||||||
void snake_step(SnakeContext *ctx, RandFunc rand);
|
|
||||||
SnakeCell snake_cell_at(const SnakeContext *ctx, char x, char y);
|
|
||||||
|
|
||||||
#endif /* SNAKE_H */
|
|
Loading…
Add table
Add a link
Reference in a new issue