SDL窗口未显示。

30

这是我的代码:

#include <iostream>
#include <SDL2/SDL.h>

int main(int argc, const char * argv[]) {

SDL_Init(SDL_INIT_VIDEO);

SDL_Window *_window;
_window = SDL_CreateWindow("Game Engine", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 700, 500, SDL_WINDOW_RESIZABLE);

SDL_Delay(20000);

SDL_DestroyWindow(_window);
SDL_Quit();

return 0;

}

我正在使用Xcode工作。我已经下载了SDL2并将库导入项目的构建阶段。我已经测试过SDL2能够正常工作。

问题是窗口从未显示。我只看到一个“旋转的Mac齿轮”,然后程序在延迟后退出。我确保窗口没有隐藏在其他地方。

有什么想法吗?


2
尝试调用 SDL_GetError 并查看其返回的内容(在 SDL_CreateWindow 之后)。 - Desaroll
返回创建的窗口,如果失败则返回NULL;调用SDL_GetError()获取更多信息。 - Pierre Emmanuel Lallemant
5个回答

107

你必须让系统有机会运行它的事件循环。

最简单的方法是自己轮询事件:

SDL_Event e;
bool quit = false;
while (!quit){
    while (SDL_PollEvent(&e)){
        if (e.type == SDL_QUIT){
            quit = true;
        }
        if (e.type == SDL_KEYDOWN){
            quit = true;
        }
        if (e.type == SDL_MOUSEBUTTONDOWN){
            quit = true;
        }
    }
}

而不是等待循环

--- 附加说明

由于这个答案仍然有帮助,也许我添加一些更多关于为什么这样做有效的信息会更好。

当程序在Mac(实际上在Windows上也是如此)上启动时,它只启动了“主线程”。这是用于设置UI的线程。与其他线程不同,“主线程”具有事件处理系统。该系统捕获诸如鼠标移动、按键、按钮点击之类的事件,然后将其排队,并让您的代码对其进行响应。Mac上的所有UI都依赖于这个事件泵存在并运行。这就是为什么如果您的代码涉及任何UI相关内容,您需要确保您不在另一个线程上的原因。

现在,在您的代码中,您初始化窗口和UI,但然后您执行SDL_Delay。这只是阻塞线程并暂停它20秒,因此什么也不会发生。而且,由于您在主线程上执行此操作,即使是处理队列中的事件也被阻塞。因此,在Mac上显示为旋转的Mac齿轮。

所以我发布的解决方案实际上继续轮询事件并处理它们。这样,您也有效地处于“空闲”状态,但一旦发布事件(如鼠标点击和键),线程就会再次唤醒并处理内容。


10
如果你发现SDL_GetError()没有报告任何错误,而SDL_UpdateWindowSurface() /等函数报告为0(没有错误),那么很可能上述代码将是解决方案,特别是在OSX上。你应该在视频设置/初始化/和初始绘制调用之后放置事件轮询循环。原始的SDL SDK示例包括轮询,但在线教程通常在处理输入时才包括此操作,这可能会引起困惑。 - Jonathan M.
6
谢谢你!非常有帮助。 - Zip Zap J
4
应将此标记为答案 :) 去除延迟,添加事件方式退出即可立即解决。 - chrisallick
这让我在macOS上使用SDL2起步,非常感谢! - Simon Bosley
我已经第三次来到这个页面,但是我忘记向下滚动足够远以获取这个答案。 - May

3
要让Xcode显示窗口,您需要加载位图图像或在窗口上显示其他内容。请注意保留HTML标签。
#include <SDL2/SDL.h>
#include <iostream>

using namespace std;

int main() {
    SDL_Window * window = nullptr;

    SDL_Surface * window_surface = nullptr;
    SDL_Surface * image_surface = nullptr;

    SDL_Init(SDL_INIT_VIDEO);

    window = SDL_CreateWindow("Window", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, SDL_WINDOW_SHOWN);

    window_surface = SDL_GetWindowSurface(window);
    image_surface = SDL_LoadBMP("image.bmp");

    SDL_BlitSurface(image_surface, NULL, window_surface, NULL);

    SDL_UpdateWindowSurface(window);

    SDL_Delay(5000);

    SDL_DestroyWindow(window);
    SDL_FreeSurface(image_surface);
    SDL_Quit();
}

1
在创建窗口之前,您需要使用SDL_Init(SDL_INIT_VIDEO)初始化SDL。

我忘记在问题中添加那行了。我现在已经编辑过了,所以那不是问题。 - Michael
好的,我在我的电脑上尝试了一下(xubuntu,x86_64),它可以工作。也许窗口没有显示是因为它没有被使用?你尝试显示一些东西(图像或简单形状或其他)了吗? - Pierre

0
所以我试图运行一个简单的代码示例,来自教程,但窗口没有显示,就像你在问题中提到的那样。
显然,为了让窗口显示,你可以在调用延迟之前调用SDL_Pump_Events。出于某种原因,我还必须将其放在一个短循环内才能使其正常工作。
有效的代码看起来像这样:
#include <SDL2/SDL.h>
#include <iostream>

using namespace std;

int main()
{
    ...

    SDL_Init(SDL_INIT_VIDEO);

    ...

    for (size_t i = 0; i < 100; i++)
    {
        SDL_PumpEvents();
    }

    SDL_Delay(5000);

    ...

    SDL_Quit();
}

更详细的解释可以在我找到这个解决方案的链接中查看。

1
这很有趣,但是如果你要做一些严肃的事情,最终你会需要一个SDL_PollEvent循环。 - HolyBlackCat

-1
请删除 sdl_delay() 并将其替换为下面提到的代码。我没有任何理由,但我自己尝试过了,它可以工作。
bool isquit = false;
SDL_Event event;
while (!isquit) {
    if (SDL_PollEvent( & event)) {
        if (event.type == SDL_QUIT) {
            isquit = true;
        }
    }
}

原因在@Toad的回答和Jonathan M.的评论中已经给出。 - wsaleem
还可以参考lazyfoo的教程:http://lazyfoo.net/tutorials/SDL/03_event_driven_programming/index.php - wsaleem

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接