如何在我的程序中正确使用 SDL2?

How do I use SDL2 in my programs correctly?

I want to make a game using SDL2, but I'm unable to compile and/or run my code, please help!

SDL2 是出了名的难以设置,它通常是有抱负的游戏开发人员首先尝试使用的库。

此 post 旨在作为设置 SDL2 常见问题的规范副本。

这个答案是关于 MinGW / GCC,而不是 Visual Studio。

此回答仅适用于Windows。


常见错误

常见错误有:

  • SDL.h: No such file or directory(编译时)
  • 各种SDL_main问题:“对SDL_main的未定义引用”、“SDL_main的冲突类型”或“参数数量与原型不匹配”等(当编译或链接)
  • undefined reference 到其他函数(链接时)
  • DLL 问题:(当 运行ning 你的程序时)
    • '??.dll' was not found
    • procedure entry point ... could not be located in ...,以及其他神秘的 DLL-related 错误
    • 程序启动时似乎什么也没做

此列表按从坏到好排序。如果您更改某些内容并遇到不同的错误,请使用此列表来判断您是让事情变得更好还是更糟。


序言

0。不要听从坏建议。

某些资源会建议您执行 #define SDL_MAIN_HANDLED#undef main。不要盲目地遵循该建议,这不是 SDL2 的用途。

如果你每件事都做对了,就永远没有必要了。首先了解预期的方法。然后你可以研究它到底做了什么,并做出明智的决定。

1.弄清楚如何直接从控制台编译,您可以稍后开始使用 IDE and/or 构建系统。 如果您使用 IDE,我建议首先确保您能够直接从控制台编译您的程序,以排除任何 IDE 配置问题。弄清楚之后,您可以在 IDE.

中使用相同的编译器选项

这同样适用于构建系统,例如 CMake。

2。下载正确的 SDL2 文件。确保您拥有正确的文件。您需要来自 here.

的名为 SDL2-devel-2.0.x-mingw.tar.gz 的存档

将它解压到任何目录,最好是靠近源代码的地方。提取到编译器安装目录通常被认为是一种不好的做法(将它们复制到 C:\Windows,这是一个糟糕的想法)。

3。了解 编译器标志 链接器标志 . 之间的区别 “标志”是您在构建时在命令行中指定的选项你的程序。当您使用单个命令时,例如g++ foo.cpp -o foo.exe,你所有的标志都被添加到同一个地方(到这个单一的命令)。

但是当您分两步构建程序时,例如:

  • g++ foo.cpp -c -o foo.o(编译中)
  • g++ foo.o -o foo.exe(链接)

您必须知道要为这两个命令中的哪一个添加标志。这些分别是“编译器标志”和“链接器标志”。

大多数 IDEs 将要求您分别指定编译器和链接器标志,因此即使您使用单个命令 now,最好知道哪个标志去哪里.

除非另有说明,标志的顺序无关紧要。


SDL.h: No such file or directory

或与包含 SDL.hSDL2/SDL.h 相关的任何类似错误。

您需要告诉您的编译器在哪里查找 SDL.h。它在您下载的 SDL 文件中(请参阅序言)。

-Ipath 添加到您的编译器标志中,其中 pathSDL.h 所在的目录。

示例:-IC:/Users/HolyBlackCat/Downloads/SDL2-2.0.12/x86_64-w64-mingw32/include/SDL2。相对路径也有效,例如-ISDL2-2.0.12/x86_64-w64-mingw32/include/SDL2.

注意路径会根据你写的方式不同#include:

  • 如果您执行 #include <SDL.h>,则路径应以 .../include/SDL2 结尾(如上)。这是推荐的方式。
  • 如果你#include <SDL2/SDL.h>,那么路径应该以.../include结束。

各种SDL_main问题

您可能会遇到几个不同的错误,提到 SDL_main,例如 undefined reference to SDL_main,或 conflicting types for 'SDL_main',或 number of arguments doesn't match prototype,等等

你需要有一个main函数。您的 main 函数必须类似于 int main(int, char **) int main() void main()。这是 SDL2 的一个怪癖,与它做 #define main SDL_main.

有关

允许添加参数名称(并且在 C 中是强制性的),例如int main(int argc, char **argv)。第二个参数也可以写成 char *[] 或名称:char *argv[]。不允许进行其他更改。

如果您的项目有多个源文件,请确保在定义 main 函数的文件中包含 SDL.h,即使它不直接使用 SDL。

解决这个问题时尽量避免#define SDL_MAIN_HANDLED#undef main,解释见序言。


undefined reference to各种函数

错误消息会提到各种 SDL_... 函数,and/or WinMain。如果它提到 SDL_main,请参阅上面的“各种 SDL_main 问题”部分。

您需要添加以下链接器标志:-lmingw32 -lSDL2main -lSDL2 -Lpath,其中 pathlibSDL2.dll.alibSDL2main.a(您下载的)所在的目录。 -l... 标志的顺序很重要。他们一定出现在任何 .c/.cpp/.o 文件之后。

示例:-LC:/Users/HolyBlackCat/Desktop/SDL2-2.0.12/x86_64-w64-mingw32/lib。相对路径也有效,例如-LSDL2-2.0.12/x86_64-w64-mingw32/lib.

当您使用 -l??? 时,链接器将查找名为 lib???.dll.alib???.a 的文件(以及一些其他变体),这就是为什么我们需要传递位置那些文件。 libmingw32.a(对应于 -lmingw32)随您的编译器一起提供,因此它已经知道在哪里可以找到它。

我添加了所有这些标志,但没有任何改变:

您可能使用了错误的 SDL .a 文件。您下载的存档包含两组文件:i686-w64-mingw32(32 位)和 x86_64-w64-mingw32(64 位)。您必须使用与您的编译器匹配的文件,它也可以是 32 位或 64 位的。

打印 (8*sizeof(void*)) 以查看您的编译器是 32 位还是 64 位。

即使您认为自己使用了正确的文件,也请尝试其他文件以确保安全。

一些 MinGW 版本可以使用 -m32-m64 标志(将它们添加到编译器和链接器标志)在 32 位和 64 位模式之间切换。

我得到undefined reference一个特定的函数:

undefined reference to WinMain

有几种可能性,所有这些都在上一节中介绍过:

  • 您忘记了 -lmingw32 and/or -lSDL2main 链接器标志。
    您必须按照以下确切顺序使用以下链接器标志,after 任何 .c/.cpp/.o 文件:-lmingw32 -lSDL2main -lSDL2
  • 您使用的 libSDL2main.a 文件与您的编译器不匹配(32 位文件与 64 位编译器,反之亦然)。

解决这个问题时尽量避免#define SDL_MAIN_HANDLED#undef main,解释见序言。

undefined reference to SDL_main

请参阅上面的“各种 SDL_main 问题”部分。


当我尝试 运行 我的应用程序时没有任何反应

假设您尝试 运行 您的应用程序,但没有任何反应。即使您尝试打印 main() 开头的内容,也不会打印出来。

Windows 有一个坏习惯,当程序从控制台启动时不显示一些 DLL-related 错误。

如果您是从控制台(或从 IDE)运行安装您的应用程序,请尝试 double-clicking 资源管理器中的 EXE。您现在很可能会看到一些 DLL-related 错误;然后参考下一节。


??.dll 未找到

复制错误消息中提到的 .dll,并将其放在您的 .exe 旁边。

如果 DLL 被称为 SDL2.dll,那么它就在您下载的 SDL 文件中(参见序言)。请注意,有两种不同的 SDL2.dll:一种是 32 位的(在 i686-w64-mingw32 目录中),另一种是 64 位的(在 x86_64-w64-mingw32 中)。选对一个,如果有必要,两个都试试。

任何其他 DLL 将在编译器的 bin 目录中(gcc.exe 所在的目录)。

您可能需要重复此过程 3-4 次,这是正常的。

有关自动确定所需 DLL 的方法,请参阅下一节。


procedure entry point ... could not be located in ... 和其他神秘的 DLL 错误

您的程序需要几个 .dll 到 运行,它发现一个错误的版本,是您安装的其他程序遗留下来的。

它在几个不同的地方寻找 DLL,但带有 .exe 的目录具有最高优先级。

您应该将您的程序使用的所有 DLL(除了系统的)复制到您的 .exe 所在的目录中。

获得所需 DLL 列表的可靠方法是盲目地复制一堆 DLL,然后删除那些不需要的:

  • 复制SDL2.dll。它在您下载的 SDL 文件中(请参阅序言)。请注意,有两种不同的 SDL2.dll:一种是 32 位的(在 i686-w64-mingw32 目录中),另一种是 64 位的(在 x86_64-w64-mingw32 中)。选对一个,如果有必要,两个都试试。

  • 从编译器的 bin 目录(gcc.exe 所在的目录)复制所有 DLL。

  • 现在您的程序应该 运行,但我们还没有完成。

  • 下载 NTLDD(或显示已用 DLL 列表的其他程序)。 运行 ntldd -R your_program.exe.

  • 输出中提到的任何 not 的 DLL 都应该从当前目录中删除。您的程序会使用剩下的所有内容。

我最终得到了以下 DLL,期待类似的东西:SDL2.dlllibgcc_s_seh-1.dlllibstdc++-6.dll(仅限 C++)、libwinpthread-1.dll.

我可以在不复制过多的情况下确定需要的DLL吗?

是的,但不太可靠。

您的程序按以下顺序在以下位置搜索 DLL:

  • 你的.exe所在的目录。
  • C:\Windows,包括它的一些子目录。
  • PATH中列出的目录。

假设您(或某些卡顿安装程序)没有将任何自定义 DLL 放入 C:\Windows,添加您的ompiler 的 bin 目录到 PATH(最好是第一个条目)并且将 SDL2.dll 放在与 .exe 相同的目录中或放入 PATH 中的某个目录应该足以让您的程序工作。

如果可行,您可以 运行 ntldd 无需事先复制任何 DLL,只复制必要的。此时您想要完全复制它们的原因(因为您的应用程序已经可以运行)是为了能够将其分发给其他人,而无需他们为其 DLL 安装编译器。跳过位于编译器 bin 目录之外的所有 DLL(SDL2.dll 除外)。

请注意 C:\Windows 中存在奇怪的 DLL 的可能性是真实存在的。例如。 Wine 倾向于将 OpenAL32.dll 放入 C:\Windows,因此如果您在 Wine 上使用 OpenAL 尝试此过程,它 失败。如果你正在制作一个自动 运行s ntldd 的 sciprt,更喜欢复制 DLL(或者至少对它们进行符号链接 - 我听说 MSYS2 可以模拟 Windows 上的符号链接?)。

我可以制作一个不依赖于任何 DLL 的 EXE 吗?

可以通过使用 -static 链接器标志来创建不依赖于任何 (non-system) .dll.exe,这称为“静态链接”。很少这样做,如果您正确执行了上述步骤,则不需要这样做。这需要一些额外的链接器标志;它们在 SDL 附带的文件 ??-w64-mingw32/lib/pkgconfig/sdl2.pc 中的 Libs.private 部分中列出。请注意,有两个文件,分别用于 x32 和 x64。


如何将我的应用分发给其他人?

按照标题为 procedure entry point ... could not be located in ... 的上一节中的步骤进行操作。


更明智的选择?

MSYS2.

它有一个包管理器,可以让你下载预构建的库,作为奖励,还有一个新版本的编译器。

从包管理器安装 SDL2。使用名为 pkg-config 的工具(也来自包管理器)自动确定所有必要的标志(pkg-config --cflags SDL2 用于编译器标志,pkg-config --libs SDL2 用于链接器标志)。

这与您在 Linux 上的体验相同(可能除了一些 DLL 管理麻烦)。


奖金 - 其他问题

  • 问:我的程序总是在我运行时打开一个控制台window,如何隐藏它?

    • A: 添加 -mwindows 到链接器标志。
  • 问:我收到 错误 'SDL_VideoMode' wasn't declared in this scope

    • A: SDL_VideoMode 来自SDL1.2,它不是较新的SDL2 的一部分。您的代码是为过时版本的 SDL 编写的。查找专门针对 SDL2 的更好的教程。
  • 问:我的程序有默认文件图标,但我想要一个自定义图标。

    • 答:您的图标必须是.ico格式。如果您的图形编辑器不支持它,请制作一系列常见尺寸的 .png(例如 16x16、32x32、48x48、64x64),然后使用 [=164= 将它们转换为单个 .ico ]:magick *.png result.ico(或使用 convert 而不是 magick)。

      创建一个扩展名为 .rc 的文件(比如 icon.rc),内容如下 MyIconName ICON "icon.ico"(其中 MyIconName 是任意名称,"icon.ico" 是图标的路径)。使用 windres -O res -i icon.rc -o icon.o 将文件转换为 .owindres 程序随编译器一起提供)。链接时指定生成的 .o 文件,例如g++ foo.cpp icon.o -o foo.exe.

      SDL2 的最新版本有一个很好的 属性 使用与 window 图标相同的图标,因此您不必使用 SDL_SetWindowIcon.

在 Mac 这是我为 XCode 遵循的(必须安装 g++):

sdl 链接:

g++ main.cpp -o main $(sdl2-config --cflags --libs)

XCODE 项目步骤:

  1. 打开终端应用程序 (macOS)

  2. 构建设置(select 'all' 和 'combined' 搜索栏输入:“搜索”)

  3. 单击“header 搜索路径(单击右侧)

  4. 添加:/usr/local/include

  5. 构建阶段 --> LINK 二进制库(点击加号)

  6. 输入SDL --> 点击“添加其他”

  7. 按:命令+SHIFT+g(带搜索栏)

  8. 输入:usr/local/Cellar

  9. 导航到:SDL2 -->2.0.8 -->lib --> libSDL2-2.2.0.dylib(确保不是快捷方式)

Visual Studio的解决方案:

为什么不使用包管理器?我使用 vcpkg,它使使用 3rd 方库变得非常容易。获取 vcpkg 源,并将其解压缩到安全的地方,例如 C:/,然后 运行 其 bootstrap 脚本 bootstrap-vcpkg.bat,这将生成 vcpkg 可执行文件。然后 运行 vcpkg integrate install 使使用 vcpkg 安装的库在 Visual Studio.

中可用

搜索您需要的图书馆:

vcpkg search sdl

imgui[sdl2-binding]                   Make available SDL2 binding
libwebp[vwebp-sdl]                    Build the vwebp viewer tool.
magnum[sdl2application]               Sdl2Application library
sdl1                 1.2.15#12        Simple DirectMedia Layer is a cross-platform development library designed to p...
sdl1-net             1.2.8-3          Networking library for SDL
sdl2                 2.0.12-1         Simple DirectMedia Layer is a cross-platform 
...

安装:vcpkg install sdl2.

现在您只需要包含 SDL2 headers,一切都会开箱即用。该库将自动链接。

您可以了解有关 vcpkg 的更多信息here