我想使用SDL2制作游戏,但是我无法编译和/或运行我的代码,请帮忙!
SDL2设置非常困难,通常是初学游戏开发者尝试使用的第一个库。
本文旨在成为SDL2安装常见问题的标准重复内容。
这个答案是关于MinGW / GCC,而不是Visual Studio。
这个答案仅适用于Windows操作系统。
常见的错误有:
SDL.h: 没有那个文件或目录
(编译时)SDL_main
问题:“未定义对SDL_main的引用”,“SDL_main的类型冲突”或“参数数量与原型不匹配”等(编译或链接时)未定义引用
(链接时)'??.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.h
或SDL2/SDL.h
相关的任何类似错误。
你需要告诉你的编译器在哪里查找SDL.h
。它在你下载的SDL文件中(见导言)。
将-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附加组件)在其头文件中使用#include "SDL.h"
。 因此,您必须使用前一选项,或将SDL2_image的include
目录复制到SDL2的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
等。
您需要有一个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.a
和 libSDL2main.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.a
或 lib???.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.dll
、libgcc_s_seh-1.dll
、libstdc++-6.dll
(仅限C++)、libwinpthread-1.dll
。
我能否确定所需的DLL文件而不复制过多的文件?
可以,但可靠性较低。
您的程序按照以下顺序在以下位置搜索DLL文件:
.exe
所在的目录。C:\Windows
,包括其中的一些子目录。假设您(或某个奇怪的安装程序)没有将任何自定义DLL文件放入C:\Windows
中,则将编译器的bin
目录添加到PATH中(最好作为第一个条目),并将SDL2.dll
放入与.exe
相同的目录或PATH中的某个目录中应该足以使您的程序正常工作。
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将它们转换为单个.ico
:magick *.png result.ico
(或者用convert
代替magick
)。
创建一个扩展名为.rc
的文件(比如icon.rc
),并包含以下内容:MyIconName ICON "icon.ico"
(其中MyIconName
是任意名称,"icon.ico"
是图标的路径)。使用windres -O res -i icon.rc -o icon.o
将文件转换为.o
(windres
程序随编译器一起提供)。在链接时指定生成的.o
文件,例如:g++ foo.cpp icon.o -o foo.exe
。
较新版本的SDL2具有一个很好的特性,即使用与窗口图标相同的图标,因此你不必使用SDL_SetWindowIcon
。
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的更多信息。
#define SDL_MAIN_HANDLED
,它可以正常工作,而且不需要手动链接任何东西。 - user4640261SDL2main
。#define SDL_MAIN_HANDLED
也可以工作,但不建议使用。推荐方式是否合理是另一个问题,有些人认为不合理(显然包括vcpkg打包人员在内)。 - HolyBlackCatg++ main.cpp -o main $(sdl2-config --cflags --libs)
XCODE项目步骤:
打开终端应用程序(macOS)
BUILD SETTINGS(选择“全部”和“组合”搜索栏输入:“搜索”)
点击“头文件搜索路径”(右侧点击)
添加:/usr/local/include
BUILD PHASES --> LINK BINARY LIBRARIES(点击加号)
输入 SDL
--> 点击“添加其他”
按下:command+SHIFT+g(打开搜索栏)
输入:usr/local/Cellar
导航到:SDL2 -->2.0.8 -->lib --> libSDL2-2.2.0.dylib(确保不是快捷方式)