如何在我的程序中正确使用 SDL2?
Posted
技术标签:
【中文标题】如何在我的程序中正确使用 SDL2?【英文标题】:How do I use SDL2 in my programs correctly? 【发布时间】:2021-01-31 10:30:01 【问题描述】:我想使用 SDL2 制作游戏,但我无法编译和/或运行我的代码,请帮忙!
众所周知,SDL2 很难设置,它通常是有抱负的游戏开发者尝试使用的第一个库。
这篇文章旨在作为设置 SDL2 的常见问题的规范副本。
【问题讨论】:
我厌倦了每周回答相同的 SDL 问题,所以我做了这个。需要帮助:我的答案侧重于 MinGW,我们可能需要一个侧重于 Visual Studio 的答案。 太好了,以后会作为那些经常报告的问题的欺骗目标。它现在在我的书签列表中;-) 这个问题的优点在于a discussion on meta。 "SDL2 是出了名的难以设置" 友好 [需要引用] :-) 如果真的有这么多这样的问题,那么我认为 SDL 项目应该改进它的文档。 【参考方案1】:此答案涉及 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 相关的问题。您可以稍后开始使用它。
2。下载正确的 SDL2 文件。确保您拥有正确的文件。您需要来自 here 的名为 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
。 顺序很重要。标志必须出现在任何 .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
有几种可能性,所有这些都在上一节中介绍过:
您忘记了-lmingw32
和/或-lSDL2main
标志。
您必须按照这个确切的顺序使用以下链接器标志,after
任何.c
/.cpp
/.o
文件:-lmingw32 -lSDL2main -lSDL2
您使用的 libSDL2main.a
文件与您的编译器不匹配(32 位文件与 64 位编译器,反之亦然)。
在解决这个问题时尽量避免#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[]
。不允许进行其他更改。
如果您的项目有多个源文件,请确保在定义 main
函数的文件中包含 SDL.h
,即使它不直接使用 SDL。
在解决这个问题时尽量避免#define SDL_MAIN_HANDLED
或#undef main
,解释见前言。
.dll
相关错误
您已经成功构建了一个.exe
,但由于提到一些.dll
s 的神秘错误而无法运行它。你快到了!
您创建的.exe
需要一些.dll
s 才能运行。如果它没有找到它们,它会告诉你并拒绝运行。这很容易。它还可以找到您安装的其他一些程序遗留下来的错误版本,然后您会得到一些非常神秘的错误。
您的程序会在不同的位置寻找.dll
s,但最可靠的解决方案是将它们放在与.exe
相同的目录中。首先搜索此目录,因此您可能在其他地方拥有的其他版本的 .dll
s 不会干扰。
你需要弄清楚你的程序需要哪个.dll
s,并将它们放在你的.exe
所在的目录中(系统.dll
s除外,你不需要需要复制)。
您可能会遇到两种类型的错误:
一个错误只是告诉您缺少 .dll
。
SDL2.dll
丢失。 — 在您下载的 SDL 文件中。
请注意,这些文件包含两个不同的 SDL2.dll
s:一个 32 位的(在 i686-w64-mingw32
目录中)和一个 64 位的(在 x86_64-w64-mingw32
中)。找一个合适的,如果有必要两个都试试。
其他一些.dll
丢失了。 — 它随您的编译器一起提供。查看您的gcc.exe
所在的目录。
如果您复制了一个.dll
并要求更多,这是正常的,您可能需要重复此操作约 3 次。
一个神秘的 .dll
相关错误。 — 您的程序在系统某处发现了错误版本的 .dll
。
您需要将所有需要的非系统.dll
s 复制到您的.exe
所在的目录中。
首先,复制SDL2.dll
(请参阅上面的“SDL2.dll
缺失”部分了解在哪里可以找到它)。
然后,从您的编译器目录(gcc.exe
所在的目录)中复制以下.dll
s:
libgcc_s_seh-1.dll
(名称可能因您的 MinGW 版本而异,但始终以 libgcc
开头)
libstdc++-6.dll
(仅适用于 C++,如果您使用 C 编写,请跳过)
libwinpthread-1.dll
(名称可能因您的 MinGW 版本而异,但始终会提及 thread
;有些版本根本不使用此名称)
对于某些 MinGW 发行版,这些可能不是正确的 .dll
s。如果您没有看到这些或复制它们没有帮助,请继续阅读。
这应该可以解决您的所有错误。如果您想了解更多或没有帮助:
更多关于.dll
s的信息
可以使用-static
链接器标志创建一个不依赖于任何(非系统).dll
s 的.exe
,这称为“静态链接”。很少这样做,如果您正确执行了上述步骤,则不需要这样做。这需要与平常不同的链接器标志(大约 20 个),请参阅 SDL 随附的 sdl2.pc
文件以了解确切的标志(它们位于 Libs.private
部分)。
在上一节中,我试图猜测您的程序依赖于.dll
s。我怎么知道的?有几种方法可以自己弄清楚:
粗略的方式:
打开控制台,cd
到你的.exe
所在的目录,然后输入set PATH=
(别担心,这不会改变你的系统PATH
设置,只会影响当前的控制台会话)。
删除您可能已经复制到那里的所有.dll
s。
现在如果你从这个控制台运行.exe
,它将只能找到系统.dll
s。它将仅在当前目录(没有)中查找非系统目录。现在您将收到明确的错误消息,说明需要什么.dll
s,您可以将它们一一复制,直到它开始工作。这样你就可以知道你的程序使用的所有非系统.dll
s。
请注意,此方法并非完全万无一失。 C:\Windows
和一些嵌套目录总是被搜索,即使在这种情况下。通常情况下,如果只有系统的.dll
s 并不重要,但如果一些糟糕的安装程序在那里复制了一个自定义的.dll
,那么这种方法将无法正常工作。
最好在C:\Windows
和嵌套目录中搜索不应该存在的.dll
s,然后删除它们。查找 SDL2.dll
和编译器附带的任何 .dll
s(在您的 gcc.exe
所在的目录中)。
文明的方式:
使用Dependency Walker 或ntldd
之类的工具。您必须检查他们的输出并确定每个.dll
是系统还是非系统。如果 .dll
随您的编译器一起提供或属于您使用的库 (SDL2.dll
),则它是非系统库。任何剩余的.dll
s 都是系统的,忽略它们。
其他问题
问:我的程序在运行时总是打开一个控制台窗口,除了我使用 SDL 打开的任何窗口。我该如何摆脱它?
答:将-mwindows
添加到链接器标志中。
问:我的程序有默认文件图标,但我想要一个自定义的。
答:您的图标必须采用.ico
格式。如果您的图形编辑器不支持它,请制作一系列常见尺寸的.png
s(例如16x16、32x32、48x48、64x64),然后使用ImageMagick将它们转换为单个.ico
:magick *.png result.ico
(或使用convert
而不是magick
)。
创建一个扩展名为.rc
的文件(比如icon.rc
),在其中输入:MAINICON ICON "icon.ico"
(第一部分是任意名称,您可以更改它;第二部分必须始终为ICON
,第三部分是图标的路径)。使用windres -i icon.rc -o icon.o
将文件转换为.o
(编译器附带windres
程序)。链接时指定生成的.o
文件,例如g++ foo.cpp icon.o -o foo.exe
.
最近版本的 SDL2 有一个很好的属性,即使用与窗口图标相同的图标,因此您不必使用 SDL_SetWindowIcon
。
问:我收到错误'SDL_VideoMode' wasn't declared in this scope
。
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 @arfneto 我想知道它是否值得与 MinGW 一起使用。似乎主要针对 MSVC,我们已经有了专门处理 MinGW 的 MSYS2。 HolyBlackCat 你是对的。它在 Visual Studio 或 CMake 下非常棒,因为它无需用户干预即可下载源代码并编译所有内容,甚至设置包含和链接。安装后,您只需要在新项目中包含标题。它可以在 Linux 或 Mac 上运行,但我还不需要它,所以我不能说太多。 +1 获取非常深入的故障排除指南!如果您使用 gcc 编译纯 C 与使用 g++ 编译 C++,那么过程有什么不同吗? @Devsman 不客气。它应该完全一样,AFAIK。【参考方案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 here 的更多信息。
【讨论】:
您缺少一个步骤:您必须手动链接 SDL2main(请参阅 this thread),或者使用一些解决方法来避免它。 @HolyBlackCat 我刚刚尝试过,它对我来说更像是 SDL 的东西。如果您在包含 SDL 标头之前添加#define SDL_MAIN_HANDLED
,它可以正常工作,无需手动链接任何内容。
请参阅上面我的长答案中的序言,第 0 部分。推荐的(SDL 开发人员)解决方案是链接SDL2main
。 #define SDL_MAIN_HANDLED
也可以,但不推荐。推荐的方式是否合理是另一个问题,有些人认为不合理(显然包括 vcpkg 打包器)。【参考方案3】:
在 Mac 上,这是我为 XCode 所遵循的(必须安装 g++):
sdl 链接:
g++ main.cpp -o main $(sdl2-config --cflags --libs)
XCODE 项目步骤:
打开终端应用程序(macOS)
BUILD SETTINGS(选择“全部”和“组合”搜索栏输入:“搜索”)
点击“标题搜索路径(右侧点击方式)
添加:/usr/local/include
构建阶段 --> 链接二进制库(点击加号)
输入SDL
--> 点击“添加其他”
按:command+SHIFT+g(带搜索栏)
输入:usr/local/Cellar
导航到:SDL2 -->2.0.8 -->lib --> libSDL2-2.2.0.dylib(确保不是快捷方式)
【讨论】:
以上是关于如何在我的程序中正确使用 SDL2?的主要内容,如果未能解决你的问题,请参考以下文章
如何在我的 Nuxt 应用程序中正确设置 bootstrap-vue?
错误在我的应用程序中不正确使用 ParentDataWidget