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

27

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

SDL2设置非常困难,通常是初学游戏开发者尝试使用的第一个库。

本文旨在成为SDL2安装常见问题的标准重复内容。


这个问题的价值涉及到在Meta上的讨论 - zcoop98
3个回答

46

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

这个答案仅适用于Windows操作系统。


常见错误

常见的错误有:

  • SDL.h: 没有那个文件或目录(编译时)
  • 各种SDL_main问题:“未定义对SDL_main的引用”,“SDL_main的类型冲突”或“参数数量与原型不匹配”等(编译或链接时)
  • 对其他函数的未定义引用(链接时)
  • DLL问题:(运行程序时)
    • '??.dll'找不到
    • 在...中找不到过程入口点...,以及其他神秘的DLL相关错误
    • 启动程序时似乎什么都没发生

此列表按照从坏到好的顺序排列。如果您更改了某些内容并获得不同的错误,请使用此列表告诉您是否使情况变得更好或更糟。


前言

0. 不要听从错误的建议。

有些资源会建议你使用#define SDL_MAIN_HANDLED#undef main。不要盲目跟随这个建议,这不是SDL2的预期使用方式。

如果你做得正确,这将永远不会成为必要的。首先学习预期的方法,然后你可以研究它到底是什么,并做出明智的决定。

1. 找出如何直接从控制台编译,稍后可以开始使用IDE和/或构建系统。 如果你正在使用IDE,请先确保你能够直接从控制台编译程序,以排除任何IDE配置问题。在找出解决方案之后,你可以在IDE中使用相同的编译器选项。

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

2. 下载正确的SDL2文件。确保你拥有正确的文件。你需要从这里下载名为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(链接)

您必须知道要向哪个命令添加标志。它们分别是“编译器标志”和“链接器标志”。

大多数IDE都需要您分别指定编译器和链接器标志,因此即使您现在使用单个命令,了解哪个标志放在哪里也很重要。

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


SDL.h: 没有那个文件或目录

或者和包括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结尾。

注意:SDL2_image(和可能其他SDL附加组件)在其头文件中使用#include "SDL.h"。 因此,您必须使用前一选项,或将SDL2_image的include目录复制到SDL2的include目录之上,以便SDL_image.hSDL.h在同一个目录中。


各种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,请参见前言中的说明。

未定义对各种函数的引用

未定义对 SDL_... 的引用

错误信息中会提到各种 SDL_... 函数和/或 WinMain。如果它提到了 SDL_main,请参考上面的“各种 SDL_main 问题”部分。如果函数名称不是以 SDL_ 开头,请参考下面的“未定义对其他函数的引用”部分。

您需要添加以下链接器标志:-lmingw32 -lSDL2main -lSDL2 -Lpath,其中path 是您下载并存放 libSDL2.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)已随编译器一起提供,因此它已经知道在哪里找到它。

我添加了所有这些标志,但没有任何变化,或者我得到了“在寻找 Y 时跳过不兼容的 X”:

你可能使用了错误的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 to WinMain

有几种可能性,所有这些都在前面的部分中涵盖了:

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

在解决此问题时,请尽量避免使用#define SDL_MAIN_HANDLED#undef main,请参见前言中的说明。

• 仅出现undefined reference to SDL_main

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

• 出现对其他函数的undefined reference

您的链接器找到并使用了libSDL2.a,但它应该找到并使用libSDL2.dll.a。当两者都可用时,默认情况下它会优先选择后者,这意味着您没有将后者复制到传递给-L的目录中。

如果您打算执行静态链接,请参阅下面的“如何将我的应用程序分发给其他人?”部分。

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

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

Windows有一个讨厌的习惯,当从控制台启动程序时不显示某些与DLL相关的错误。

如果您是从控制台(或IDE)运行应用程序,请尝试在资源管理器中双击EXE。很可能现在会看到一些与DLL相关的错误;然后请参考下一节中的内容。


??.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,这是由于您安装了其他程序而导致的。

程序会在几个不同的位置查找DLL文件,但包含.exe的目录具有最高优先级。

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

获取所需DLL文件列表的可靠方法是盲目地复制一堆DLL文件,然后删除那些被证明是不必要的文件:

  • 复制SDL2.dll。它在你下载的SDL文件中(见前言)。请注意,有两个不同的SDL2.dll:一个是32位的(在i686-w64-mingw32目录下),另一个是64位的(在x86_64-w64-mingw32目录下)。如果需要,请获取正确的版本并尝试两个。

  • 将编译器的bin目录中的所有DLL复制到当前目录(即gcc.exe所在的目录)。

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

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

  • 其输出未提及的任何DLL均应从当前目录中移除。您的程序使用其余的所有东西。

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

我能否确定所需的DLL文件而不复制过多的文件?

可以,但可靠性较低。

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

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

假设您(或某个奇怪的安装程序)没有将任何自定义DLL文件放入C:\Windows中,则将编译器的bin目录添加到PATH中(最好作为第一个条目),并将SDL2.dll放入与.exe相同的目录或PATH中的某个目录中应该足以使您的程序正常工作。

如果这个方法有效,那么你可以在不事先复制任何DLL的情况下运行ntldd,并且只复制必要的DLL。之所以此时您仍需要复制它们(因为您的应用程序已经运行),是为了将其分发给其他人,而无需让他们安装编译器的DLL。跳过位于编译器的bin目录之外的任何DLL(除了SDL2.dll)。
请注意,在C:\Windows中存在奇怪的DLL的可能性是真实的。例如,Wine倾向于将OpenAL32.dll放入C:\Windows,因此如果您尝试在Wine上使用OpenAL进行此过程,则会失败。如果您正在编写自动运行ntldd的脚本,请优先考虑复制DLL(或至少建立符号链接-我听说MSYS2可以在Windows上模拟符号链接)。 我能创建一个不依赖于任何DLL的EXE吗?

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


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

请按照上一节中的步骤,标题为在...中找不到过程入口点...


一个更明智的选择?

MSYS2

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

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

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


奖励 - 其他问题

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

    • 答:在链接器标志中添加-mwindows
  • 问:我得到了错误'SDL_VideoMode' wasn't declared in this scope

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

    • 答:你的图标必须是.ico格式。如果你的图形编辑器不支持它,请制作一系列常见大小的.png(例如16x16、32x32、48x48、64x64),然后使用ImageMagick将它们转换为单个.icomagick *.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具有一个很好的特性,即使用与窗口图标相同的图标,因此你不必使用SDL_SetWindowIcon


2
时代在变化。现在你也可以使用微软自己的包管理器,它也可以在Linux和MacOS上工作,并编译SDL(或几乎任何重要的包)。这个东西叫做vcpkg,是开源的。vcpkg - arfneto
@arfneto,我在想是否值得在MinGW中使用它。它似乎主要面向MSVC,而我们已经有了专门处理MinGW的MSYS2。 - HolyBlackCat
HolyBlackCat,你说得对。在Visual Studio或CMake下使用它非常棒,因为它会下载源代码并编译所有内容,无需用户干预,甚至设置包含和链接。一旦安装完成,您只需要在新项目中包含头文件即可。它适用于Linux或Mac,但我还没有使用过,所以无法多说。 - arfneto
1
@HolyBlackCat 感谢您详细的解释,这是我过去5年中读过的最好的文章之一。它完全解决了我所有的烦恼。 - Gulaev Valentin

2

Visual Studio的解决方案:

为什么不使用包管理器?我使用vcpkg,它使得使用第三方库变得非常简单。获取vcpkg源代码并将其提取到安全位置(例如C:/),然后运行其引导脚本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头文件,一切都可以开箱即用。库将自动链接。

您可以在这里了解有关vcpkg的更多信息。


2
你缺少一步:你必须手动链接SDL2main(参见此线程),或使用某些解决方法来避免它。 - HolyBlackCat
@HolyBlackCat,我刚试了一下,对我来说更像是SDL的事情。如果在包括SDL头文件之前添加#define SDL_MAIN_HANDLED,它可以正常工作,而且不需要手动链接任何东西。 - user4640261
请参见我上面长篇回答中的序言,第0部分。SDL开发人员推荐的解决方案是链接SDL2main#define SDL_MAIN_HANDLED也可以工作,但不建议使用。推荐方式是否合理是另一个问题,有些人认为不合理(显然包括vcpkg打包人员在内)。 - HolyBlackCat

1
在Mac上,我按照以下步骤进行XCode编程(必须安装g ++):
g++ main.cpp -o main $(sdl2-config --cflags --libs)

XCODE项目步骤:

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

  2. BUILD SETTINGS(选择“全部”和“组合”搜索栏输入:“搜索”)

  3. 点击“头文件搜索路径”(右侧点击)

  4. 添加:/usr/local/include

  5. BUILD PHASES --> LINK BINARY LIBRARIES(点击加号)

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

  7. 按下:command+SHIFT+g(打开搜索栏)

  8. 输入:usr/local/Cellar

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


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