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

Hol*_*Cat 16 c c++ sdl sdl-2

我想使用 SDL2 制作游戏,但我无法编译和/或运行我的代码,请帮助!

众所周知,SDL2 很难设置,它通常是有抱负的游戏开发人员尝试使用的第一个库。

这篇文章旨在作为设置 SDL2 的常见问题的规范副本。

Hol*_*Cat 25

这个答案涉及 MinGW/GCC,而不是 Visual Studio。

该答案主要涉及 Windows。其他操作系统上的事情更容易,请参阅答案底部的摘要。

我试图让这个答案保持简单,这样即使是新手也可以使用它。


常见错误

您可能会遇到的常见错误是:

  • SDL.h: No such file or directory (编译时)
  • undefined reference to 各种功能(链接时)
  • 与神秘.dll相关的错误(运行程序时)。

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

有关如何解决所有这些问题,请参阅下面的答案。


序言

在我们修复错误之前,我想告诉您一些事情。

0. 不要听从不好的建议。

一些资源会建议你做#define SDL_MAIN_HANDLED#undef main不要那样做。这些解决方案偏离了 SDL2 的预期使用方式,并且通过正确的设置,它们从来都不是必需的。

有些人认为这些比预期的方式更好,但我建议先学习预期的方式,然后了解有什么不同,然后做出明智的决定。

1.弄清楚如何直接从控制台编译,你可以稍后开始使用IDE。 如果您使用的是 IDE,我建议首先确保您能够直接从控制台编译您的程序,以排除任何 IDE 配置问题。弄清楚之后,您可以在 IDE 中使用相同的编译器选项。

如果您刚开始使用 CMake,我还建议您避免使用 CMake,以排除任何与 CMake 相关的问题。您可以稍后开始使用它。

2. 下载正确的 SDL2 文件。确保您拥有正确的文件。您需要SDL2-devel-2.0.x-mingw.tar.gz此处调用的存档。

将它解压到任何目录中,最好靠近源代码。直接解压到编译器目录通常被认为是一种不好的做法。

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

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

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

您必须知道要向其中添加标志的两个命令中的哪一个。需要添加到第一个命令的标志是“编译器标志”,需要添加到第二个命令的标志是“链接器标志”。在下面的答案中,当告诉您添加标志时,我将指定它是编译器标志还是链接器标志。

大多数 IDE 会要求您分别指定编译器和链接器标志,因此即使您现在使用单个命令,也最好知道哪个标志放在哪里。

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


SDL.h: No such file or directory

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

您需要告诉编译器位于哪个目录SDL.h中。它位于您下载的 SDL 文件中(请参阅序言)。

将以下内容添加到您的编译器标志中:-I,后跟一个目录。

例子:-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.

undefined reference to 各种功能

错误消息将提及各种SDL_...函数(通常是您在程序中使用的函数)和/或WinMain. 如果提到SDL_main,请参阅undefined reference to SDL_main下面的“仅”部分。

您需要添加以下链接器标志:-lmingw32 -lSDL2main -lSDL2. 顺序很重要。该标志必须出现在任何AFTER .c/ .cpp/.o文件。

写入-lSDL2main -lSDL2告诉链接器使用您下载的 SDL 文件中包含的名为libSDL2main.a, libSDL2.a(或libSDL2.dll.a)的文件。您还需要一个标志来告诉链接器在哪里查找这些.a文件。将以下内容添加到您的链接器标志中:-L,后跟目录。

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

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

如果您这样做了,但仍然出现相同的undefined reference错误,则您可能使用了错误的 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

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

  • 你忘记了-lSDL2main国旗。
  • libSDL2main.a您使用的文件与您的编译器不匹配(32 位文件与 64 位编译器,反之亦然)。
  • 你把-lSDL2main国旗一些左.c/ .cpp/.o文件。它必须始终在右侧。

尽量避免#define SDL_MAIN_HANDLED#undef main在解决此问题时,请参阅序言以获取解释。

undefined reference to SDL_main

你需要有一个main功能。你的main函数必须看起来像int main(int, char **). 不是 int main()不是 void main()。这是 SDL2 的怪癖之一。

允许添加任何参数名称,例如int main(int argc, char **argv). 此外,第二个参数可以写成char *[]或带有名称:char *argv[]。不允许进行其他更改。

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

尽量避免#define SDL_MAIN_HANDLED#undef main在解决此问题时,请参阅序言以获取解释。


.dll-相关错误

您已经成功构建了一个.exe,但由于提到某些.dlls的神秘错误而无法运行它。您快到了!

.exe你需要做一些.dlls到运行。如果它没有找到它们,它会告诉你并拒绝运行。这很简单。它还可以找到您安装的其他一些程序遗留下来的错误版本,然后您会得到一些非常神秘的错误。

您的程序将.dll在不同位置查找s,但最可靠的解决方案是将它们与.exe. 首先搜索此目录,因此.dll您可能在别处拥有的s 的其他版本不会干扰。

你需要找出哪些.dll是你的程序的需求,并把他们的目录中的.exeIS(除系统.dllS,你不需要复制)。

您可能会遇到两种类型的错误:

  • 一个错误只是告诉您.dll缺少什么。

    • SDL2.dll不见了。— 它位于您下载的 SDL 文件中。

      请注意,这些文件包含两个不同的SDL2.dlls:一个 32 位的(在i686-w64-mingw32目录中)和一个 64 位的(在x86_64-w64-mingw32. 选择正确的一个,如有必要,请尝试两者。

    • 还有一些.dll不见了。— 它随您的编译器一起提供。查看你所在的目录gcc.exe

      如果您复制一个.dll并要求更多,这是正常的,您可能需要重复约 3 次。

  • 一个神秘的.dll相关错误。— 您的程序.dll在系统中某处发现了错误版本的 a 。

    您需要将.dll它需要的所有非系统s复制到您所在的目录中.exe

    首先,复制SDL2.dll(有关在SDL2.dll哪里可以找到它,请参阅上面的“丢失”部分)。

    然后,.dll从编译器目录(所在目录gcc.exe)复制以下s :

    • libgcc_s_seh-1.dll(名称可能因您的 MinGW 版本而异,但始终以 开头libgcc
    • libstdc++-6.dll (仅适用于 C++,如果您用 C 编写,请跳过)
    • libwinpthread-1.dll(名称可能因您的 MinGW 版本而异,但会始终提及thread;有些版本根本不使用它)

    对于某些 MinGW 发行版,这些可能不是正确.dll的。如果您没有看到这些或复制它们没有帮助,请继续阅读。

那应该可以解决您的所有错误。如果您想了解更多信息或没有帮助:

有关.dlls 的更多信息

通过使用链接器标志可以创建.exe不依赖于任何(非系统).dlls 的 s -static,这称为“静态链接”。很少这样做,如果您正确地执行了上述步骤,则不需要这样做。这需要与通常不同的链接器标志(大约 20 个),请参阅sdl2.pcSDL 随附的文件以了解确切的标志(它们在Libs.private部分)。

在上一节中,我试图猜测.dll你的程序依赖什么。我怎么知道?有几种方法可以自己解决:

  • 粗暴的方法:

    打开控制台,cd到您所在的目录.exe,然后键入set PATH=(别担心,这不会更改您的系统PATH设置,只会影响当前的控制台会话)。

    删除.dll您可能已经在那里复制的任何s。

    现在,如果您.exe从此控制台运行,它将只能找到系统.dlls。它只会在当前目录(没有)中查找非系统目录。现在您将得到明确的错误消息,说明.dll需要什么,您可以一一复制它们,直到它开始工作。这样你就会知道.dll你的程序使用的所有非系统。

    请注意,此方法并非完全万无一失。C:\Windows并且始终搜索某些嵌套目录,即使在这种情况下也是如此。通常情况下,系统中是否只有.dlls并不重要,但是如果某些蹩脚的安装程序.dll在其中复制了自定义,则此方法将无法正常工作。

    最好在C:\Windows嵌套目录中搜索.dll不应该存在的 s,然后将其删除。查找与您的编译器一起提供的SDL2.dll任何.dlls(在您所在的目录中gcc.exe)。

  • 文明方式:

    使用像Dependency Walkerntldd. 您必须查看他们的输出并确定每个输出.dll是系统还是非系统。如果 a.dll随您的编译器一起提供或属于您使用的库 ( SDL2.dll),则它是非系统的。任何剩余的.dlls 都是系统的,忽略它们。


其他问题

  • 问:除了我使用 SDL 打开的任何窗口之外,我的程序在运行时总是会打开一个控制台窗口。我该如何摆脱它?

    • A:添加-mwindows到链接器标志。
  • 问:我的程序有默认文件图标,但我想要一个自定义图标

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

      创建一个带有.rc扩展名的文件(假设为icon.rc),在其中键入以下内容:(MAINICON ICON "icon.ico"第一部分是任意名称,您可以更改它;第二部分必须始终是ICON,第三部分是图标的路径)。将文件转换为.ousing windres -i icon.rc -o icon.o(该windres程序随您的编译器一起提供)。.o链接时指定生成的文件,例如g++ foo.cpp icon.o -o foo.exe.

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

  • 问:我收到错误'SDL_VideoMode' wasn't declared in this scope

    • A:SDL_VideoMode存在于 SDL1.2 中,但不是 SDL2 的一部分。您的代码是为过时的 SDL1.2 编写的。查找处理新 SDL2 的更好教程。

我不在 Windows 上,我有什么不同之处?

上面的整个答案主要针对 MinGW 和 Windows。如果您不在 Windows 上,事情会变得更简单:

  • .dll相关问题。

  • 无需手动下载 SDL2,它应该在您的包管理器中。

  • 无需考虑使用哪个编译器或链接器标志:

    • pkg-config --cflags sdl2 会告诉你编译器标志。

    • pkg-config --libs sdl2会告诉你链接器标志。--static如果您想要静态链接的标志,请添加。

  • 您不需要-lSDL2main,并且没有与SDL_main.

另一方面:

  • 将您的程序分发给其他人变得更加困难,您不能.dll随程序一起发送一堆s。

  • 设置自定义图标的过程是不同的。


替代方法:在 Windows 上使用类似 Linux 的环境

上一节关于安装库和确定编译器标志的部分听起来是否很方便?你也可以在 Windows 上使用它。

下载 MSYS2。您将拥有一个包管理器(您可以从中安装 SDL)、最新的编译器和一组从 Linux 移植的常用实用程序(包括pkg-config)。这解决了大多数特定于 Windows 的问题。

  • 好吧,时代变了。您还可以使用 Microsoft 自己的包管理器(也适用于 Linux 和 MacO)并编译 SDL(或几乎任何重要的包)。这个东西叫做 vcpkg 并且是开源的。[vcpkg](https://github.com/microsoft/vcpkg#vcpkg-overview) (3认同)
  • @HolyBlackCat 感谢您的详细解释,这是我过去 5 年读过的最好的女巫之一。绝对解决了我所有的烦恼。 (2认同)