【SDL游戏编程入门第四卷】事件系统介绍+键盘事件
一、SDL事件系统介绍
我们之前在代码中有声明 SDL_Event 类型的变量用来接收事件
事件可以是 按键事件、鼠标事件、摇杆事件、窗口事件等
我们最开始使用的事件是 SDL_QUIT
,该事件在用户点击窗口关闭按钮时触发
1. 事件队列
在主循环的顶部,我们有事件循环。这样做的目的是继续处理事件队列,直到它为空。
当您按下某个键、移动鼠标或触摸触摸屏时,事件将放入事件队列中。
然后,事件队列将按照事件发生的顺序存储它们,等待您处理它们。当您想要了解发生了哪些事件以便可以处理它们时,您可以通过调用 SDL_PollEvent
轮询事件队列以获取最新事件。SDL_PollEvent
从事件队列中获取最新的事件,并将事件中的数据放入我们传递到函数的参数 SDL_Event
中。
SDL_PollEvent
将继续从队列中删除事件,直到它为空。当队列为空时,SDL_PollEvent
将返回 0。所以之前代码所做的是继续轮询事件,关闭事件队列,直到它为空。如果事件队列中的事件是 SDL_QUIT
事件(即用户点击窗口关闭按钮 X 离开窗口时的事件),我们将 quit 标志设置为 true,以便我们可以退出应用程序。
二、按键事件
1. 介绍
窗外只是 SDL 能够处理的事件之一。游戏中大量使用的另一种输入类型是键盘。
SDL_Event 包含 SDL_KeyboardEvent 事件,其中包含 SDL_Keysym,其中包含有关按下的键的信息 SDL_Keycode。
- 新增代码
在上一卷的基础上,声明了纹理的位置
// 控制纹理坐标
int gTexturePosX = 100; // 纹理 X 坐标
int gTexturePosY = 100; // 纹理 Y 坐标
int gTextureMovementSpeed = 20; // 纹理移动速度
在事件循环中,添加了简单的按键处理
// 处理事件
while (SDL_PollEvent(&e))
{
if (e.type == SDL_QUIT)
quit = true;
else if (e.type == SDL_KEYDOWN) // 处理按键按下事件
{
switch (e.key.keysym.sym)
{ // 按键移动纹理坐标位置
case SDLK_w: gTexturePosY -= gTextureMovementSpeed; break;
case SDLK_s: gTexturePosY += gTextureMovementSpeed; break;
case SDLK_a: gTexturePosX -= gTextureMovementSpeed; break;
case SDLK_d: gTexturePosX += gTextureMovementSpeed; break;
}
}
}
2. 示例代码
/* 此源代码版权归 AnnihilateSword (2022-*)所有,未经书面许可不得转载。*/
// 使用 SDL 和 iostream
#include <SDL.h>
#include <iostream>
// 链接库
#pragma comment(lib, "SDL2.lib")
#pragma comment(lib, "SDL2main.lib")
// 屏幕尺寸常量
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
bool Init(); // 启动 SDL 并创建窗口
bool LoadMedia(); // 加载媒体
SDL_Surface* LoadSurface(const char* path); // 加载单个图像
void Close(); // 清理资源,关闭窗口以及SDL
SDL_Window* gWindow = nullptr; // 主窗口
SDL_Surface* gWindowSurface = nullptr; // 主窗口关联的表面
SDL_Surface* gTexture = nullptr; // 需要加载的纹理(表面)
// 控制纹理坐标
int gTexturePosX = 100; // 纹理 X 坐标
int gTexturePosY = 100; // 纹理 Y 坐标
int gTextureMovementSpeed = 20; // 纹理移动速度
int main(int argc, char* argv[]) // 必须要填写此参数,不然会出现链接错误
{
// 初始化 SDL
if (Init())
{
// 窗口循环
SDL_Event e;
bool quit = false;
while (quit == false)
{
// 处理事件
while (SDL_PollEvent(&e))
{
if (e.type == SDL_QUIT)
quit = true;
else if (e.type == SDL_KEYDOWN) // 处理按键按下事件
{
switch (e.key.keysym.sym)
{ // 按键移动纹理坐标位置
case SDLK_w: gTexturePosY -= gTextureMovementSpeed; break;
case SDLK_s: gTexturePosY += gTextureMovementSpeed; break;
case SDLK_a: gTexturePosX -= gTextureMovementSpeed; break;
case SDLK_d: gTexturePosX += gTextureMovementSpeed; break;
}
}
}
// --- rendering -----------------------------------------------------------------------
// 将表面填充为蓝色
SDL_FillRect(gWindowSurface, nullptr, SDL_MapRGB(gWindowSurface->format, 51, 76, 204));
// 应用纹理
SDL_Rect dstRect{ gTexturePosX, gTexturePosY, 341, 256 };
// SDL_BlitSurface(gTexture, nullptr, gWindowSurface, &dstRect); // 不缩放
SDL_BlitScaled(gTexture, nullptr, gWindowSurface, &dstRect);
// 更新表面
SDL_UpdateWindowSurface(gWindow);
}
}
// 清理资源
Close();
return 0;
}
/**
* @brief 启动 SDL 并创建窗口
*/
bool Init()
{
// 初始化 SDL
if (SDL_Init(SDL_INIT_VIDEO) < 0)
{
std::cout << "[Error]: SDL could not initialize! SDL_Error: " << SDL_GetError() << std::endl;
return false;
}
else
{
// 加载媒体资源
if (!LoadMedia())
{
std::cout << "[Error]: Failed to load Media!" << std::endl;
}
else
{
// 创建窗口
gWindow = SDL_CreateWindow("HelloSDL", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_SHOWN);
if (!gWindow)
{
std::cout << "[Error]: Window could not be created! SDL_Error: " << SDL_GetError() << std::endl;
}
else
{
// 获取窗口关联的表面
gWindowSurface = SDL_GetWindowSurface(gWindow);
if (!gWindowSurface)
return false;
}
}
}
return true;
}
/**
* @brief 加载媒体
*/
bool LoadMedia()
{
gTexture = LoadSurface(R"(res\textures\Naruto.bmp)");
if (!gTexture)
{
std::cout << "[Error]: Failed to load texture!" << std::endl;
return false;
}
return true;
}
/**
* @brief 加载单个图像
* @param path:资源路径
*/
SDL_Surface* LoadSurface(const char* path)
{
SDL_Surface* loadedSurface = SDL_LoadBMP(path);
if (!loadedSurface)
std::cout << "[Error]: Unable to load image " << path << " SDL Error: " << SDL_GetError() << std::endl;
return loadedSurface;
}
/**
* @brief 清理资源,关闭窗口以及SDL
*/
void Close()
{
if (gTexture)
{
// 清理纹理资源
SDL_FreeSurface(gTexture);
gTexture = nullptr;
}
// 销毁窗口
SDL_DestroyWindow(gWindow);
// 退出 SDL
SDL_Quit();
}
运行程序应该可以使用 WASD 按键实现上下左右移动纹理图片
3. 运行结果
本节内容就到这里了,下卷会继续分享 SDL 的基本使用
The End.