如何在我的程序中正确使用 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.hSDL2/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.alibSDL2.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,但由于提到一些.dlls 的神秘错误而无法运行它。你快到了!

您创建的.exe 需要一些.dlls 才能运行。如果它没有找到它们,它会告诉你并拒绝运行。这很容易。它还可以找到您安装的其他一些程序遗留下来的错误版本,然后您会得到一些非常神秘的错误。

您的程序会在不同的位置寻找.dlls,但最可靠的解决方案是将它们放在与.exe 相同的目录中。首先搜索此目录,因此您可能在其他地方拥有的其他版本的 .dlls 不会干扰。

你需要弄清楚你的程序需要哪个.dlls,并将它们放在你的.exe所在的目录中(系统.dlls除外,你不需要需要复制)。

您可能会遇到两种类型的错误:

一个错误只是告诉您缺少 .dll

SDL2.dll 丢失。 — 在您下载的 SDL 文件中。

请注意,这些文件包含两个不同的 SDL2.dlls:一个 32 位的(在 i686-w64-mingw32 目录中)和一个 64 位的(在 x86_64-w64-mingw32 中)。找一个合适的,如果有必要两个都试试。

其他一些.dll 丢失了。 — 它随您的编译器一起提供。查看您的gcc.exe 所在的目录。

如果您复制了一个.dll 并要求更多,这是正常的,您可能需要重复此操作约 3 次。

一个神秘的 .dll 相关错误。 — 您的程序在系统某处发现了错误版本的 .dll

您需要将所有需要的非系统.dlls 复制到您的.exe 所在的目录中。

首先,复制SDL2.dll(请参阅上面的“SDL2.dll 缺失”部分了解在哪里可以找到它)。

然后,从您的编译器目录(gcc.exe 所在的目录)中复制以下.dlls:

libgcc_s_seh-1.dll(名称可能因您的 MinGW 版本而异,但始终以 libgcc 开头) libstdc++-6.dll(仅适用于 C++,如果您使用 C 编写,请跳过) libwinpthread-1.dll(名称可能因您的 MinGW 版本而异,但始终会提及 thread;有些版本根本不使用此名称)

对于某些 MinGW 发行版,这些可能不是正确的 .dlls。如果您没有看到这些或复制它们没有帮助,请继续阅读。

这应该可以解决您的所有错误。如果您想了解更多或没有帮助:

更多关于.dlls的信息

可以使用-static 链接器标志创建一个不依赖于任何(非系统).dlls 的.exe,这称为“静态链接”。很少这样做,如果您正确执行了上述步骤,则不需要这样做。这需要与平常不同的链接器标志(大约 20 个),请参阅 SDL 随附的 sdl2.pc 文件以了解确切的标志(它们位于 Libs.private 部分)。

在上一节中,我试图猜测您的程序依赖于.dlls。我怎么知道的?有几种方法可以自己弄清楚:

粗略的方式:

打开控制台,cd到你的.exe所在的目录,然后输入set PATH=(别担心,这不会改变你的系统PATH设置,只会影响当前的控制台会话)。

删除您可能已经复制到那里的所有.dlls。

现在如果你从这个控制台运行.exe,它将只能找到系统.dlls。它将仅在当前目录(没有)中查找非系统目录。现在您将收到明确的错误消息,说明需要什么.dlls,您可以将它们一一复制,直到它开始工作。这样你就可以知道你的程序使用的所有非系统.dlls。

请注意,此方法并非完全万无一失。 C:\Windows 和一些嵌套目录总是被搜索,即使在这种情况下。通常情况下,如果只有系统的.dlls 并不重要,但如果一些糟糕的安装程序在那里复制了一个自定义的.dll,那么这种方法将无法正常工作。

最好在C:\Windows 和嵌套目录中搜索不应该存在的.dlls,然后删除它们。查找 SDL2.dll 和编译器附带的任何 .dlls(在您的 gcc.exe 所在的目录中)。

文明的方式:

使用Dependency Walker 或ntldd 之类的工具。您必须检查他们的输出并确定每个.dll 是系统还是非系统。如果 .dll 随您的编译器一起提供或属于您使用的库 (SDL2.dll),则它是非系统库。任何剩余的.dlls 都是系统的,忽略它们。


其他问题

问:我的程序在运行时总是打开一个控制台窗口,除了我使用 SDL 打开的任何窗口。我该如何摆脱它?

答:将-mwindows 添加到链接器标志中。

问:我的程序有默认文件图标,但我想要一个自定义的。

答:您的图标必须采用.ico 格式。如果您的图形编辑器不支持它,请制作一系列常见尺寸的.pngs(例如16x16、32x32、48x48、64x64),然后使用ImageMagick将它们转换为单个.icomagick *.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相关的问题。

另一方面:

将你的程序分发给其他人变得更加困难,你不能只用你的程序发送一堆.dlls。

设置自定义图标的过程不同。


替代方法:在 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?

如何在我的 ASP.NET 应用程序中为文件定义正确的路径

错误在我的应用程序中不正确使用 ParentDataWidget

高 CPU 使用率 OpenGL Ver 3.1 + SDL2

Xcode:可执行文件找不到 SDL2.framework

如何在我的应用程序中获取 JSON 数据