配置
CMake配置SDL2
cmake_minimum_required(VERSION 3.0)
project(SDL2Game)
set(CMAKE_CXX_STANDARD 17)
# SDL2 SDL_IMAGE都放在了一起
set(SDL2_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/SDL2/include)
set(SDL2_LIBRARY_DIR ${CMAKE_CURRENT_SOURCE_DIR}/SDL2/lib)
add_executable(SDL2Game main.cpp)
target_include_directories(SDL2Game PUBLIC ${SDL2_INCLUDE_DIR})
target_link_directories(SDL2Game PUBLIC ${SDL2_LIBRARY_DIR})
target_link_libraries(SDL2Game PUBLIC mingw32 SDL2main SDL2 SDL2_image)
使用
基础使用 窗口 表面 事件
#include <SDL2/SDL.h>
#include <iostream>
// main 函数被声明为了宏,所以argc和argv必须要有
// 这个函数会被SDL中内置的main函数调用
int main(int argc, char** argv)
{
// SDL中的窗口
SDL_Window* window = nullptr;
// SDL中的表面
SDL_Surface* window_surface = nullptr;
SDL_Surface* img_surface = nullptr;
SDL_Init(SDL_INIT_VIDEO);
// 创建一个窗口
window = SDL_CreateWindow("Test Name", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
640, 480, SDL_WINDOW_SHOWN);
// 获取窗口的表面
window_surface = SDL_GetWindowSurface(window);
// 加载图片
img_surface = SDL_LoadBMP("../test.bmp");
if (img_surface == nullptr)
{
std::cout << "load img error: " << SDL_GetError() << std::endl;
}
else
{
// 将图片绘制到窗口的表面上
SDL_BlitSurface(img_surface, nullptr, window_surface, nullptr);
SDL_UpdateWindowSurface(window);
}
// 基于事件的管理机制
bool is_running = true;
SDL_Event ev;
while (is_running)
{
while (SDL_PollEvent(&ev) != 0)
{
if (ev.type == SDL_QUIT)
{
is_running = false;
}
}
SDL_UpdateWindowSurface(window);
}
SDL_FreeSurface(img_surface);
SDL_DestroyWindow(window);
SDL_Quit();
return 0;
}
常用事件
// 退出
if (ev.type == SDL_QUIT)
{
is_running = false;
}
// 按键按下和弹起
else if (ev.type == SDL_KEYUP || ev.type == SDL_KEYDOWN)
{
std::cout << "key down: " << ev.key.keysym.sym << ", " << ev.key.timestamp << std::endl;
}
// 鼠标左键 鼠标右键 按下
else if (ev.type == SDL_MOUSEBUTTONUP || ev.type == SDL_MOUSEBUTTONDOWN)
{
if (ev.button.button == SDL_BUTTON_LEFT || ev.button.button == SDL_BUTTON_RIGHT)
{
std::cout << "mouse button down: " <<
ev.button.button << ", " << ev.key.timestamp << std::endl;
}
}
// 鼠标移动
else if (ev.type == SDL_MOUSEMOTION)
{
std::cout << ev.motion.x << ", " << ev.motion.y << std::endl;
}
文字输入
SDL_StartTextInput();
std::string text;
while (is_running)
{
while (SDL_PollEvent(&ev) != 0)
{
if (ev.type == SDL_QUIT)
{
is_running = false;
}
else if (ev.type == SDL_TEXTINPUT || ev.type == SDL_KEYDOWN)
{
// clear
system("cls");
if (ev.type == SDL_KEYDOWN && ev.key.keysym.sym == SDLK_BACKSPACE &&
text.length() > 0)
{
text = text.substr(0, text.length() - 1);
}
else if (ev.type == SDL_TEXTINPUT)
{
text += ev.text.text;
}
std::cout << text << std::endl;
}
}
SDL_UpdateWindowSurface(window);
}
SDL_StopTextInput();
图片优化 && 图片缩放
SDL_Surface* OptimizedSurface(const std::string& filepath, SDL_Surface* windows_surface)
{
SDL_Surface* image_surface = SDL_LoadBMP(filepath.c_str());
if (!image_surface)
{
std::cout << "error: " << SDL_GetError() << std::endl;
return nullptr;
}
auto converted_surface =
SDL_ConvertSurface(image_surface, windows_surface->format, 0);
SDL_FreeSurface(image_surface);
return converted_surface;
}
// 图片缩放
window = SDL_CreateWindow("Test Name", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
640, 480, SDL_WINDOW_SHOWN);
SDL_Surface* windows_surface = SDL_GetWindowSurface(window);
SDL_Surface* image = OptimizedSurface("../test.bmp", windows_surface);
SDL_Rect rect{0, 0, 640, 480};
SDL_BlitScaled(image, nullptr, windows_surface, &rect);
Texture && Render
这么多Destroy, 感觉可以上RAII了。
#include <SDL2/SDL.h>
#include <iostream>
SDL_Texture* LoadTexture(const std::string& filepath, SDL_Renderer* target_render)
{
SDL_Surface* image_surface = SDL_LoadBMP(filepath.c_str());
if (!image_surface)
{
std::cout << "error: " << SDL_GetError() << std::endl;
return nullptr;
}
return SDL_CreateTextureFromSurface(target_render, image_surface);
}
// main 函数被声明为了宏,所以argc和argv必须要有
// 这个函数会被SDL中内置的main函数调用
int main(int argc, char** argv)
{
// SDL中的窗口
SDL_Window* window = nullptr;
SDL_Init(SDL_INIT_VIDEO);
// 创建一个窗口
window = SDL_CreateWindow("Test Name", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
640, 480, SDL_WINDOW_SHOWN);
// SDL_RENDERER_ACCELERATED 硬件加速
SDL_Renderer* render_target = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
SDL_Texture* texture = LoadTexture("../test.bmp", render_target);
bool is_running = true;
SDL_Event ev;
while (is_running)
{
while (SDL_PollEvent(&ev) != 0)
{
if (ev.type == SDL_QUIT)
{
is_running = false;
}
// 清除
SDL_RenderClear(render_target);
// 将texture绘制到render上
SDL_RenderCopy(render_target, texture, nullptr, nullptr);
// 绘制
SDL_RenderPresent(render_target);
}
SDL_UpdateWindowSurface(window);
}
SDL_DestroyWindow(window);
SDL_DestroyRenderer(render_target);
SDL_DestroyTexture(texture);
SDL_Quit();
return 0;
}
加载 PNG IMG等
需要使用SDLImage https://www.libsdl.org/projects/SDL_image/
这样即可使用IMG_Load
加载图片
使用前需要进行初始化
auto init_flag = IMG_INIT_PNG | IMG_INIT_JPG;
if (IMG_Init(init_flag) != init_flag)
{
std::cout << "init error" << std::endl;
}
精灵移动
https://retro-sprite-creator.nihey.org/character/new
https://web.archive.org/web/20141219071009/http://www.famitsu.com/freegame/tool/chibi/index1.html
#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <iostream>
#include <array>
SDL_Texture* LoadTexture(const std::string& filepath, SDL_Renderer* target_render)
{
SDL_Surface* image_surface = IMG_Load(filepath.c_str());
if (!image_surface)
{
std::cout << "error: " << SDL_GetError() << std::endl;
return nullptr;
}
return SDL_CreateTextureFromSurface(target_render, image_surface);
}
// main 函数被声明为了宏,所以argc和argv必须要有
// 这个函数会被SDL中内置的main函数调用
int main(int argc, char** argv)
{
const int FPS = 60;
float frame_time = 0;
uint32_t prev_time = 0;
uint32_t current_time = 0;
float delta_time = 0;
float move_speed = 100.0f;
SDL_Init(SDL_INIT_VIDEO);
IMG_Init(IMG_INIT_PNG);
SDL_Window* window = SDL_CreateWindow("Test Name", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
640, 480, SDL_WINDOW_SHOWN);
// SDL_Renderer感觉是一个图层,index代表图层的下标从-1开始
// SDL_RENDERER_ACCELERATED 硬件加速
SDL_Renderer* render_target = SDL_CreateRenderer(window, -1,
SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
// 图层背景色
SDL_SetRenderDrawColor(render_target, 0xFF, 0, 0, 0);
SDL_Texture* texture = LoadTexture("../sprite-sheet.png", render_target);
int texture_width, texture_height, frame_width, frame_height;
SDL_QueryTexture(texture, nullptr, nullptr, &texture_width, &texture_height);
frame_width = texture_width / 3;
frame_height = texture_height / 4;
SDL_Rect player_rect{ 0, 0, frame_width, frame_height};
SDL_Rect player_pos{ 0, 0, 32, 32};
// 下 左 右 上
std::array<int, 4> rect_y = {0, frame_width, frame_width * 2, frame_width * 3};
bool is_running = true;
SDL_Event ev;
while (is_running)
{
prev_time = current_time;
current_time = SDL_GetTicks();
delta_time = (current_time - prev_time) / 1000.0f;
while (SDL_PollEvent(&ev) != 0)
{
if (ev.type == SDL_QUIT)
{
is_running = false;
}
}
const uint8_t* key_state = SDL_GetKeyboardState(nullptr);
// 如果这里不强制转换 而是用float 向右和下移动 会比 向上和左移动 要慢
int move_length = static_cast<int>(move_speed * delta_time);
// float move_length = move_speed * delta_time;
if (key_state[SDL_SCANCODE_RIGHT])
{
player_pos.x += move_length;
player_rect.y = rect_y[2];
}
else if (key_state[SDL_SCANCODE_LEFT])
{
player_pos.x -= move_length;
player_rect.y = rect_y[1];
}
else if (key_state[SDL_SCANCODE_UP])
{
player_pos.y -= move_length;
player_rect.y = rect_y[3];
}
else if (key_state[SDL_SCANCODE_DOWN])
{
player_pos.y += move_length;
player_rect.y = rect_y[0];
}
frame_time += delta_time;
if (frame_time >= 0.25f)
{
frame_time = 0;
player_rect.x += frame_width;
if (player_rect.x >= texture_width)
{
player_rect.x = 0;
}
}
// 清除图层
SDL_RenderClear(render_target);
// 将texture绘制到render上
SDL_RenderCopy(render_target, texture, &player_rect, &player_pos);
// 绘制图层
SDL_RenderPresent(render_target);
}
SDL_DestroyWindow(window);
SDL_DestroyRenderer(render_target);
SDL_DestroyTexture(texture);
SDL_Quit();
return 0;
}