SDL3.0 学习随笔:其一
1、随谈
十年前,我在安卓端使用C4droid学习编写C/C++程序获得了巨大成功,并且该工具支持Turbo C、GCC、G++编译工具、支持SDL2.0(Simple DirectMedia Layer)、QT,并支持导出为apk程序。那个时候 贴吧还是各路大神菜鸟一起交流的圣地,物是人非呀。
2、回忆
之前学习SDL2.0的时候,遇到很多SDL本身的bug,却无可奈何,导致程序很不稳定,如今想起来,想试着学习一下SDL3.0看看能不能比较方便的写一些2D的可视化程序。
本篇文档完全基于VS 2019 MSVC或者VS 2022 MSVC,其间可能会涉及到其他平台,会进行提示。
3、正题:
SDL官方网站:Simple DirectMedia Layer - Homepage
SDL GitHub:Simple Directmedia Layer · GitHub
4、编译
在git上下载好 SDL-release-3.2.20.zip,以及cmake-gui
打开cmake-gui,依次配置好第一行Browse Source源代码目录和第三行Browse build 输出目录
点击左下角的Configure,就会出现工具链选择,选好vs和x64,然后确定,点击Genrate,会生成解决方案,点击open project,选择用vs打开,即可进行编译,编译debug和release两种。
然后,打开源码目录,找到 include,把里面SDL3整个目录移到一个自己的目录,把out里面的debug和release目录整个也复制到自己的目录里。
5、在自己的项目中导入该库
这个自己琢磨吧,就是设置C++附加包含目录为include,附加库目录分别为debug和release编译选项分别配置为刚刚输出的目录,然后在项目邮件属性,配置属性的调试中设置环境为:PATH=D:\proj_2022\thirdParty\SDL3\lib\Release;%PATH%
debug生成就设debug路径。
6、初步学习解读
代码是抄的SDL官网的一段代码删减了一些,大致解读一下我学到的一些概念
#define SDL_MAIN_USE_CALLBACKS 1 /* use the callbacks instead of main() */
#include <SDL3/SDL_main.h>
此两行会帮我们生成main函数,并且整个程序会自动调用SDL_AppInit(初始化窗口等)、SDL_AppEvent(产生事件会调用这个)、SDL_AppIterate(每一帧该绘制时会调用,不保证调用的时间间隔,具体请查看SDL3/SDL_AppIterate - SDL Wiki)、SDL_AppQuit(退出时清理程序)
#include <SDL3/SDL.h>
SDL头文件
/* snake.c ... *//** Logic implementation of the Snake game. It is designed to efficiently* represent the state of the game in memory.** 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>#pragma comment(lib, "SDL3.lib")#define STEP_RATE_IN_MILLISECONDS 125typedef enum
{SNAKE_DIR_RIGHT,SNAKE_DIR_UP,SNAKE_DIR_LEFT,SNAKE_DIR_DOWN
} SnakeDirection;static SDL_AudioStream* stream = NULL;
static Uint8* wav_data = NULL;
static Uint32 wav_data_len = 0;
static SDL_Joystick* joystick = NULL;typedef struct
{SDL_Window* window;SDL_Renderer* renderer;Uint64 last_step;
} AppState;static void set_rect_xy_(SDL_FRect* r, short x, short y)
{r->x = (float)(x * 24);r->y = (float)(y * 24);
}static SDL_AppResult handle_hat_event_(Uint8 hat) {switch (hat) {}return SDL_APP_CONTINUE;
}SDL_AppResult SDL_AppIterate(void* appstate)
{AppState* as = (AppState*)appstate;const Uint64 now = SDL_GetTicks();SDL_FRect r;int ct;if (SDL_GetAudioStreamQueued(stream) < (int)wav_data_len) {/* feed more data to the stream. It will queue at the end, and trickle out as the hardware needs more data. */SDL_PutAudioStreamData(stream, wav_data, wav_data_len);}while ((now - as->last_step) >= STEP_RATE_IN_MILLISECONDS) {// 在此处写经过125ms后蛇移动逻辑as->last_step += STEP_RATE_IN_MILLISECONDS;}r.w = r.h = 24;SDL_SetRenderDrawColor(as->renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);SDL_RenderClear(as->renderer);// 绘制场景set_rect_xy_(&r, 0, 0);SDL_SetRenderDrawColor(as->renderer, 80, 80, 255, SDL_ALPHA_OPAQUE);SDL_SetRenderDrawColor(as->renderer, 0, 128, 0, SDL_ALPHA_OPAQUE);SDL_RenderFillRect(as->renderer, &r);SDL_SetRenderDrawColor(as->renderer, 255, 255, 0, SDL_ALPHA_OPAQUE); /*head*/set_rect_xy_(&r, 1, 1);SDL_RenderFillRect(as->renderer, &r);SDL_RenderPresent(as->renderer);return SDL_APP_CONTINUE;
}static const struct
{const char* key;const char* value;
} extended_metadata[] =
{{ SDL_PROP_APP_METADATA_URL_STRING, "https://examples.libsdl.org/SDL3/demo/01-snake/" },{ SDL_PROP_APP_METADATA_CREATOR_STRING, "SDL team" },{ SDL_PROP_APP_METADATA_COPYRIGHT_STRING, "Placed in the public domain" },{ SDL_PROP_APP_METADATA_TYPE_STRING, "game" }
};SDL_AppResult SDL_AppInit(void** appstate, int argc, char* argv[])
{size_t i;SDL_AudioSpec spec;char* wav_path = NULL;if (!SDL_SetAppMetadata("Example Snake game", "1.0", "com.example.Snake")) {return SDL_APP_FAILURE;}for (i = 0; i < SDL_arraysize(extended_metadata); i++) {if (!SDL_SetAppMetadataProperty(extended_metadata[i].key, extended_metadata[i].value)) {return SDL_APP_FAILURE;}}if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK | SDL_INIT_AUDIO)) {SDL_Log("Couldn't initialize SDL: %s", SDL_GetError());return SDL_APP_FAILURE;}AppState* as = (AppState*)SDL_calloc(1, sizeof(AppState));if (!as) {return SDL_APP_FAILURE;}*appstate = as;if (!SDL_CreateWindowAndRenderer("examples/demo/snake", 24 * 24, 18 * 24, SDL_WINDOW_BORDERLESS, &as->window, &as->renderer)) {return SDL_APP_FAILURE;}as->last_step = SDL_GetTicks();/* Load the .wav file from wherever the app is being run from. */SDL_asprintf(&wav_path, "%smu.wav", SDL_GetBasePath()); /* allocate a string of the full file path */if (!SDL_LoadWAV(wav_path, &spec, &wav_data, &wav_data_len)) {SDL_Log("Couldn't load .wav file: %s", SDL_GetError());return SDL_APP_FAILURE;}SDL_free(wav_path); /* done with this string. *//* Create our audio stream in the same format as the .wav file. It'll convert to what the audio hardware wants. */stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec, NULL, NULL);if (!stream) {SDL_Log("Couldn't create audio stream: %s", SDL_GetError());return SDL_APP_FAILURE;}/* SDL_OpenAudioDeviceStream starts the device paused. You have to tell it to start! */SDL_ResumeAudioStreamDevice(stream);return SDL_APP_CONTINUE;
}SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event)
{switch (event->type) {case SDL_EVENT_QUIT:return SDL_APP_SUCCESS;case SDL_EVENT_JOYSTICK_ADDED:if (joystick == NULL) {joystick = SDL_OpenJoystick(event->jdevice.which);if (!joystick) {SDL_Log("Failed to open joystick ID %u: %s", (unsigned int)event->jdevice.which, SDL_GetError());}}break;case SDL_EVENT_JOYSTICK_REMOVED:if (joystick && (SDL_GetJoystickID(joystick) == event->jdevice.which)) {SDL_CloseJoystick(joystick);joystick = NULL;}break;case SDL_EVENT_JOYSTICK_HAT_MOTION://return handle_hat_event_(ctx, event->jhat.value);case SDL_EVENT_KEY_DOWN://return handle_key_event_(ctx, event->key.scancode);default:break;}return SDL_APP_CONTINUE;
}void SDL_AppQuit(void* appstate, SDL_AppResult result)
{if (joystick) {SDL_CloseJoystick(joystick);}if (appstate != NULL) {AppState* as = (AppState*)appstate;SDL_DestroyRenderer(as->renderer);SDL_DestroyWindow(as->window);SDL_free(as);}
}