提问人:K. Russell Smith 提问时间:11/11/2023 最后编辑:wohlstadK. Russell Smith 更新时间:11/11/2023 访问量:120
将C++对象方法作为SDL_EventFilter传递
Pass C++ object method as SDL_EventFilter
问:
我正在尝试使用 SDL2 在 C++ 中创建游戏,当然我想检查窗口大小调整事件......因为我有一个类处理了所有与 SDL 相关的内容:Game
bool Game::SetupSDL()
{
if (SDL_Init(SDL_INIT_VIDEO) < 0)
{
std::cout << "SDL initialization failed: " << SDL_GetError() << std::endl;
return false;
}
window = SDL_CreateWindow(
"SDL",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
640, 480,
SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
if (window == NULL)
{
std::cout << "SDL window creation failed: " << SDL_GetError() << std::endl;
return false;
}
if (TTF_Init() < 0)
{
return false;
}
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
SDL_AddEventWatch(OnResize, window);
return true;
}
初始化方式如下:OnResize
int Game::OnResize(void *data, SDL_Event *event)
{
if (event->type == SDL_WINDOWEVENT && event->window.event == SDL_WINDOWEVENT_RESIZED)
{
SetScale();
states[state]->OnResize(*this);
}
return 0;
}
但是,当我尝试编译它时,我得到以下信息:
error: cannot convert 'Game::OnResize' from type 'int (Game::)(void*, SDL_Event*)' to type 'SDL_EventFilter {aka int (*)(void*, SDL_Event*)}'
SDL_AddEventWatch(this->OnResize, window);
因此,我似乎在将实例方法作为...尝试重铸它导致编译或运行时错误,我该如何解决这个问题?SDL_FilterEvent
答:
0赞
Alex
11/11/2023
#1
您收到该错误是因为该语言不知道如何访问要调用该方法的类的正确实例。
您可以将 Game::OnResize 替换为 Game 类之外的自由浮动函数,该函数可以访问指向游戏实例的指针,沿 SDL 事件数据转发,也可以在 Game 类中使用静态方法。
如果你想把它全部保留在你的类中(就像我倾向于做的那样),你可以将 Game::OnResize 替换为一个静态方法,该方法可以访问指向你的游戏的指针,然后创建一个新的非静态方法来实际执行事件的处理。然后,静态方法可以将事件转发到非静态方法。
喜欢这个:
// =========================================================================
// game.h
// =========================================================================
class Game
{
public:
// public stuff...
private:
// private stuff...
// replace OnResize with this:
static int OnSDLEvent(void *data, SDL_Event *event);
// new function:
int HandleSDLEvent(void *data, SDL_Event *event);
};
// =========================================================================
// game.cpp
// =========================================================================
// somewhere at the top...
Game *g_Game{}; // global pointer to the game, only in this file
Game::Game()
{
// let's make g_Game point to our actual game instance
// in your game's constructor.
// or it could be in Game::SetupSDL() or wherever...
// it just needs to happen before adding your event watch.
// this is so the static method has an instance to work with.
g_Game = this;
}
// here's the static method that gets called by SDL
int Game::OnSDLEvent(void *data, SDL_Event *event)
{
// just pass along the event data.
return g_Game->HandleSDLEvent(data, event);
}
// and here's where you actually handle the event!
int Game::HandleSDLEvent(void *data, SDL_Event *event)
{
if (event->type == SDL_WINDOWEVENT && event->window.event == SDL_WINDOWEVENT_RESIZED)
{
SetScale();
states[state]->OnResize(*this);
}
return 0;
}
// finally, feed SDL with our static method we created
bool Game::SetupSDL()
{
// setup code here...
// now we pass in our static method
SDL_AddEventWatch(OnSDLEvent, window);
// etc.
}
评论
0赞
wohlstad
11/11/2023
一般来说,在上下文中使用全局不是一个好主意。如果您有 2 个实例(并且您希望每个实例都获得其回调)怎么办?Game*
Game
3赞
wohlstad
11/11/2023
#2
问题:
为了向 SDL_AddEventWatch
注册事件回调,
您需要以函数的形式提供类型为 SDL_EventFilter
:
int (*SDL_EventFilter)(void *userdata, SDL_Event *event);
这应该是一个自由函数,或者是一个类的静态方法。它不能是非静态方法,因为此类方法需要实例(指针)才能调用。Game
this
解决方案:
为了使用非静态方法作为回调,您可以利用该参数作为上下文。userdata
注册事件处理程序时,将 (即 ) 与静态方法一起传递:this
Game*
userdata
static int Game::OnResizeEventCallback(void *userdata, SDL_Event *event); // see implementation below
// ...
//---------------------------------------vvvvv-
SDL_AddEventWatch(OnResizeEventCallback, this);
然后当你得到回调时,转换回 ,并调用非静态方法:userdata
Game*
static int Game::OnResizeEventCallback(void *userdata, SDL_Event *event)
{
// Retrieve your instance:
Game* pThis = reinterpret_cast<Game*>(userdata);
// Call your non-static method with it:
pThis->OnResize(event);
}
评论
1赞
Alex
11/11/2023
这个答案是正确的。我假设将窗口指针传递到 SDL_AddEventWatch 中,如 OP 的代码所示,是正确的 - 事实并非如此。
评论
this
void*
void*
Game*
this