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

How do I use SDL2 in my programs correctly?

提问人:HolyBlackCat 提问时间:10/17/2020 最后编辑:HolyBlackCat 更新时间:5/15/2023 访问量:16934

问:

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

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

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

C++ C SDL、 SDL-2

评论

0赞 zcoop98 11/11/2020
这个问题的优点涉及在元的讨论中。

答:

48赞 HolyBlackCat 10/17/2020 #1

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

此答案仅适用于 Windows。


常见错误

常见错误有:

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

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


序言

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

一些资源会建议你做或。不要盲目地遵循这个建议,这不是 SDL2 的本意。#define SDL_MAIN_HANDLED#undef main

如果你做的一切都是正确的,那就永远没有必要了。首先了解预期的方法。然后,您可以研究这到底是做什么的,并做出明智的决定。

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: No such file or directory

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

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

添加到编译器标志中,其中的目录是所在的目录。-IpathpathSDL.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 插件)在其标头中使用。因此,您必须使用前一个选项,或者将 SDL2_image 的目录复制到 SDL2 目录的顶部,以便与 .#include "SDL.h"includeincludeSDL_image.hSDL.h


各种问题SDL_main

你可以得到几个不同的错误,比如,或,或,或,等等。SDL_mainundefined reference to SDL_mainconflicting types for 'SDL_main'number of arguments doesn't match prototype

你需要有一个功能。您的函数必须如下所示。不是,不是.这是 SDL2 的一个怪癖,与它做.mainmainint main(int, char **)int main()void main()#define main SDL_main

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

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

尽量避免或在解决此问题时,请参阅序言中的说明。#define SDL_MAIN_HANDLED#undef main


undefined reference to功能多样

undefined reference to SDL_...

错误消息将提及各种函数,和/或 .如果提到,请参阅上面的“各种问题”部分。如果函数名称不以 开头,请参阅下面的“对其他函数的未定义引用”部分。SDL_...WinMainSDL_mainSDL_mainSDL_

您需要添加以下链接器标志:,where 是 和(已下载)所在的目录。-l... 标志的顺序很重要。它们必须出现在任何 .c/.cpp/.o 文件之后。-lmingw32 -lSDL2main -lSDL2 -LpathpathlibSDL2.dll.alibSDL2main.a

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

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

我添加了所有这些标志,但没有任何变化,或者我在搜索 Y 时跳过了不兼容的 X

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

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

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

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

我得到对特定函数的未定义引用

•只undefined reference to WinMain

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

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

尽量避免或在解决此问题时,请参阅序言中的说明。#define SDL_MAIN_HANDLED#undef main

•只undefined reference to SDL_main

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

• 其他功能undefined reference

您的链接器已找到并使用,但它应该是查找和使用。当两者都可用时,默认情况下它更喜欢后者,这意味着您没有将后者复制到您传递到的目录。libSDL2.alibSDL2.dll.a-L

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


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

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

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

如果从控制台(或 IDE)运行应用,请尝试在资源管理器中双击 EXE。您现在很可能会看到一些与DLL相关的错误;然后查阅以下部分之一。


??.dll未找到

复制错误消息中提到的内容,并将其放在 ..dll.exe

如果调用 DLL,则它位于您下载的 SDL 文件中(请参阅序言)。请注意,有两个不同的 s:一个是 32 位的(在目录中),另一个是 64 位的(在 中)。选择正确的,如有必要,请尝试两者。SDL2.dllSDL2.dlli686-w64-mingw32x86_64-w64-mingw32

任何其他 DLL 都将位于编译器的目录(所在的目录)中。bingcc.exe

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

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


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

您的程序需要几个小时才能运行,并且它发现了一个错误的版本,这是您安装的其他程序遗留下来的。.dll

它在多个不同的位置查找 DLL,但具有 DLL 的目录具有最高的优先级。.exe

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

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

  • 复制。它位于您下载的 SDL 文件中(请参阅序言)。请注意,有两个不同的 s:一个是 32 位的(在目录中),另一个是 64 位的(在 中)。选择正确的,如有必要,请尝试两者。SDL2.dllSDL2.dlli686-w64-mingw32x86_64-w64-mingw32

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

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

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

  • 应从当前目录中删除其输出中提及的任何 DLL。您的程序使用剩余的所有内容。

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

我能否在不复制过多 DLL 的情况下确定所需的 DLL?

是的,但它不太可靠。

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

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

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

如果这可行,则可以在不事先复制任何 DLL 的情况下运行,并且仅复制必要的 DLL。此时,您之所以想要复制它们(因为您的应用程序已经运行),是为了能够将其分发给其他人,而无需他们为其 DLL 安装编译器。 跳过位于编译器目录之外的任何 DLL(除外)。ntlddbinSDL2.dll

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

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

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


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

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


更理智的选择?

MSYS2

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

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

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


奖金 - 其他问题

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

    • 答:添加到链接器标志。-mwindows
  • 问:我收到错误“SDL_VideoMode”未在此范围内声明

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

    • 答:您的图标必须采用该格式。如果您的图形编辑器不支持它,请制作一系列常见尺寸(例如 16x16、32x32、48x48、64x64),然后使用 ImageMagick:(或用代替 )。.ico.png.icomagick *.png result.icoconvertmagick

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

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

评论

2赞 arfneto 10/17/2020
好吧,时代变了。您还可以使用 Microsoft 自己的包管理器,该管理器也适用于 Linux 和 MacOs,并编译 SDL(或几乎任何重要的包)。这个东西叫做 vcpkg,是开源的。vcpkg
0赞 HolyBlackCat 10/17/2020
@arfneto我想知道它是否值得与 MinGW 一起使用。似乎主要针对 MSVC,我们已经有了专门处理 MinGW 的 MSYS2。
0赞 arfneto 10/17/2020
HolyBlackCat,你是对的。它在 Visual Studio 或 CMake 下非常有用,因为它无需用户干预即可下载源代码并编译所有内容,甚至可以设置包含和链接。安装后,您只需要在新项目中包含标题即可。它适用于 Linux 或 Mac,但我还不需要它,所以我不能说太多。
2赞 Gulaev Valentin 5/13/2023
@HolyBlackCat感谢您的详细解释,这是我过去 5 年读过的最好的女巫之一。它绝对解决了我所有的烦恼。
1赞 Skyy 10/17/2020 #2

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

SDL 链接:

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

XCODE项目步骤:

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

  2. 构建设置(选择“全部”和“组合”搜索栏,输入:“搜索”)

  3. 点击“标题搜索路径(右侧点击方式)

  4. 加:/usr/local/include

  5. 构建阶段 --> 链接二进制库(单击加号)

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

  7. 按:++(带搜索栏)commandSHIFTg

  8. 输入:usr/local/Cellar

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

2赞 user4640261 10/21/2020 #3

Visual Studio 的解决方案:

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

搜索您需要的库:

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赞 HolyBlackCat 10/21/2020
您缺少一个步骤:您必须手动链接 SDL2main(请参阅此线程),或使用一些解决方法来避免它。
0赞 10/21/2020
@HolyBlackCat我刚刚尝试过,对我来说,这更像是 SDL 的事情。如果您在包含 SDL 标头之前添加,它可以正常工作,无需手动链接任何内容。#define SDL_MAIN_HANDLED
0赞 HolyBlackCat 10/21/2020
请参阅我上面长答案中的序言,第 0 部分。(SDL 开发人员)推荐的解决方案是链接 . 也可以,但不建议这样做。推荐的方式是否合理是另一个问题,有些人认为不合理(显然包括 vcpkg 打包器)。SDL2main#define SDL_MAIN_HANDLED