提问人:HolyBlackCat 提问时间:10/17/2020 最后编辑:HolyBlackCat 更新时间:5/15/2023 访问量:16934
如何在程序中正确使用 SDL2?
How do I use SDL2 in my programs correctly?
问:
我想使用 SDL2 制作游戏,但我无法编译和/或运行我的代码,请帮忙!
众所周知,SDL2 很难设置,它通常是有抱负的游戏开发人员尝试使用的第一个库。
本文旨在作为设置 SDL2 常见问题的规范副本。
答:
这个答案是关于 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.h
SDL2/SDL.h
您需要告诉编译器在哪里查找 .它位于您下载的 SDL 文件中(请参阅序言)。SDL.h
添加到编译器标志中,其中的目录是所在的目录。-Ipath
path
SDL.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"
include
include
SDL_image.h
SDL.h
各种问题SDL_main
你可以得到几个不同的错误,比如,或,或,或,等等。SDL_main
undefined reference to SDL_main
conflicting types for 'SDL_main'
number of arguments doesn't match prototype
你需要有一个功能。您的函数必须如下所示。不是,不是.这是 SDL2 的一个怪癖,与它做.main
main
int main(int, char **)
int main()
void main()
#define main SDL_main
添加参数名称是允许的(在 C 中是强制性的),例如 .此外,第二个参数可以写成或写成名称:。不允许进行其他更改。int main(int argc, char **argv)
char *[]
char *argv[]
如果您的项目有多个源文件,请确保包含在定义函数的文件中,即使它不直接使用 SDL。SDL.h
main
尽量避免或在解决此问题时,请参阅序言中的说明。#define SDL_MAIN_HANDLED
#undef main
undefined reference to
功能多样
•undefined reference to SDL_...
错误消息将提及各种函数,和/或 .如果提到,请参阅上面的“各种问题”部分。如果函数名称不以 开头,请参阅下面的“对其他函数的未定义引用”部分。SDL_...
WinMain
SDL_main
SDL_main
SDL_
您需要添加以下链接器标志:,where 是 和(已下载)所在的目录。-l...
标志的顺序很重要。它们必须出现在任何 .c
/.cpp
/.o
文件之后。-lmingw32 -lSDL2main -lSDL2 -Lpath
path
libSDL2.dll.a
libSDL2main.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.a
lib???.a
libmingw32.a
-lmingw32
我添加了所有这些标志,但没有任何变化,或者我在搜索 Y 时跳过了不兼容的 X
:
您可能使用了错误的 SDL 文件。您下载的存档包含两组文件:(32 位)和(64 位)。您必须使用与编译器匹配的文件,编译器也可以是 32 位或 64 位。.a
i686-w64-mingw32
x86_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.a
libSDL2.dll.a
-L
如果您打算执行静态链接,请参阅下面的“如何将我的应用分发给他人”部分。
当我尝试运行我的应用程序时没有任何反应
假设您尝试运行您的应用程序,但没有任何反应。即使您尝试在开头打印某些内容,它也不会打印。main()
Windows 有一个令人讨厌的习惯,即从控制台启动程序时不显示一些与 DLL 相关的错误。
如果从控制台(或 IDE)运行应用,请尝试在资源管理器中双击 EXE。您现在很可能会看到一些与DLL相关的错误;然后查阅以下部分之一。
??.dll
未找到
复制错误消息中提到的内容,并将其放在 ..dll
.exe
如果调用 DLL,则它位于您下载的 SDL 文件中(请参阅序言)。请注意,有两个不同的 s:一个是 32 位的(在目录中),另一个是 64 位的(在 中)。选择正确的,如有必要,请尝试两者。SDL2.dll
SDL2.dll
i686-w64-mingw32
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,然后删除那些不必要的 DLL:
复制。它位于您下载的 SDL 文件中(请参阅序言)。请注意,有两个不同的 s:一个是 32 位的(在目录中),另一个是 64 位的(在 中)。选择正确的,如有必要,请尝试两者。
SDL2.dll
SDL2.dll
i686-w64-mingw32
x86_64-w64-mingw32
从编译器的目录(所在的目录)复制所有 DLL。
bin
gcc.exe
现在你的程序应该运行了,但我们还没有完成。
下载 NTLDD(或显示已用 DLL 列表的其他程序)。跑。
ntldd -R your_program.exe
应从当前目录中删除其输出中未提及的任何 DLL。您的程序使用剩余的所有内容。
我最终得到了以下DLL,期望类似的东西:,,(仅限C++),.SDL2.dll
libgcc_s_seh-1.dll
libstdc++-6.dll
libwinpthread-1.dll
我能否在不复制过多 DLL 的情况下确定所需的 DLL?
是的,但它不太可靠。
程序按以下顺序在以下位置搜索 DLL:
- your 所在的目录。
.exe
C:\Windows
,包括其某些子目录。- PATH 中列出的目录。
假设您(或某些卡顿的安装程序)没有将任何自定义 DLL 放入 ,将编译器的目录添加到 PATH(最好是第一个条目)并放入与 PATH 相同的目录或放入 PATH 中的某个目录应该足以让您的程序正常工作。C:\Windows
bin
SDL2.dll
.exe
如果这可行,则可以在不事先复制任何 DLL 的情况下运行,并且仅复制必要的 DLL。此时,您之所以想要复制它们(因为您的应用程序已经运行),是为了能够将其分发给其他人,而无需他们为其 DLL 安装编译器。 跳过位于编译器目录之外的任何 DLL(除外)。ntldd
bin
SDL2.dll
请注意,存在奇怪 DLL 的可能性是真实的。例如,Wine 倾向于放入 ,所以如果你在 Wine 上使用 OpenAL 尝试这个过程,它将失败。如果您正在制作一个自动运行的 sciprt,最好复制 DLL(或者至少对它们进行符号链接 - 我听说 MSYS2 可以在 Windows 上模拟符号链接?C:\Windows
OpenAL32.dll
C:\Windows
ntldd
我可以制作一个不依赖于任何 DLL 的 EXE 吗?
通过使用链接器标志,可以创建一个不依赖于任何(非系统)的 s,这称为“静态链接”。这种情况很少发生,如果您正确地执行了上述步骤,则不需要这样做。这需要一些额外的链接器标志;它们列在 SDL 随附的文件中的“部分”。请注意,有两个文件,分别用于 x32 和 x64。.exe
.dll
-static
??-w64-mingw32/lib/pkgconfig/sdl2.pc
Libs.private
如何将我的应用分发给其他人?
按照上一节中的步骤操作,标题为 。procedure entry point ... could not be located in ...
更理智的选择?
有 MSYS2。
它有一个包管理器,可让您下载预构建的库,以及作为奖励的编译器的新版本。
从其包管理器安装 SDL2。使用一个名为(也来自包管理器)的工具自动确定所有必要的标志(用于编译器标志,用于链接器标志)。pkg-config
pkg-config --cflags SDL2
pkg-config --libs SDL2
这与您在 Linux 上的体验相同(也许除了一些 DLL 管理麻烦)。
奖金 - 其他问题
问:我的程序在运行时总是打开一个控制台窗口,如何隐藏它?
- 答:添加到链接器标志。
-mwindows
- 答:添加到链接器标志。
问:我收到错误
“SDL_VideoMode”未在此范围内声明
。- 答:来自 SDL1.2,它不是较新的 SDL2 的一部分。您的代码是为过时的 SDL 版本编写的。查找专门针对 SDL2 的更好教程。
SDL_VideoMode
- 答:来自 SDL1.2,它不是较新的 SDL2 的一部分。您的代码是为过时的 SDL 版本编写的。查找专门针对 SDL2 的更好教程。
问:我的程序有默认的文件图标,但我想要一个自定义的图标。
答:您的图标必须采用该格式。如果您的图形编辑器不支持它,请制作一系列常见尺寸(例如 16x16、32x32、48x48、64x64),然后使用 ImageMagick:(或用代替 )。
.ico
.png
.ico
magick *.png result.ico
convert
magick
创建一个扩展名为(例如,)的文件,其中包含以下内容(其中是任意名称,是图标的路径)。将文件转换为 using(该程序随编译器一起提供)。在链接时指定生成的文件,例如 .
.rc
icon.rc
MyIconName ICON "icon.ico"
MyIconName
"icon.ico"
.o
windres -O res -i icon.rc -o icon.o
windres
.o
g++ foo.cpp icon.o -o foo.exe
最新版本的 SDL2 具有一个很好的属性,即使用与窗口图标相同的图标,因此您不必使用 .
SDL_SetWindowIcon
评论
在 Mac 上,这是我遵循的 XCode(必须安装 g++):
SDL 链接:
g++ main.cpp -o main $(sdl2-config --cflags --libs)
XCODE项目步骤:
打开终端应用程序 (macOS)
构建设置(选择“全部”和“组合”搜索栏,输入:“搜索”)
点击“标题搜索路径(右侧点击方式)
加:
/usr/local/include
构建阶段 --> 链接二进制库(单击加号)
输入 --> 点击“添加其他”
SDL
按:++(带搜索栏)commandSHIFTg
输入:
usr/local/Cellar
导航到:SDL2 -->2.0.8 -->lib --> libSDL2-2.2.0.dylib(确保不是快捷方式)
Visual Studio 的解决方案:
为什么不使用包管理器呢?我使用 vcpkg,它使使用第三方库变得非常容易。获取 vcpkg 源,并将其解压缩到安全的地方,例如 ,然后运行其引导脚本,这将生成可执行文件。然后运行以使使用 vcpkg 安装的库在 Visual Studio 中可用。C:/
bootstrap-vcpkg.bat
vcpkg
vcpkg 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 的更多信息。
评论
#define SDL_MAIN_HANDLED
SDL2main
#define SDL_MAIN_HANDLED
评论