虽然笔者是在Rider下实现的,不过VS下也是可以用的
最近为了折腾这SDL2,可是废了点时间,尝试用C/C++/Go/C#进行开发,结果发现:
- C/C++环境好设置,但是cmake配置麻烦(对于没用过的我来说),而且我受够了引入文件总要include
- Go最省心,go mod包管理永远的神!sdl2库的api封装也是用起来最舒服的
- C#在windows下很方便,但是切到mac就各种毛病,奈何C#做游戏太香了,写好了核心类库,以后port到unity也方便。
C#在mac下用SDL2开发游戏,在油管和各种论坛上有好多帖子,但是没有一篇能让我真正运行起来,看到窗口的,着实有点劝退...
几番探索下,我终于成功了,这里给大家分享下,希望至少能省下下大伙半天时间...
话不多说,开始吧!
创建项目
咱们先不依赖任何编译器,这样好多参数也就不用选了,方便大家有统一的环境。
咱们用命令来创建项目
创建项目目录
项目名称SDL2_TEST
$ mkdir SDL2_TEST
$ cd SDL2_TEST
用命令创建项目
$ dotnet new console
结果发现报错,该命令不存在,可是咱们可是安装了dotnet的,咋回事?
原来这个命令躲在其他地方,我们要链接下
$ ln -s /usr/local/share/dotnet/dotnet /usr/local/bin/
再次运行,发现可以了。
$ dotnet new console
利用homebrew安装sdl2
其实我另一篇文章提到了怎么做,一客不烦二主,直接一篇文章搞定吧!
依次执行下面的命令就好了
- 安装国内镜像的Homebrew,一路往下YES就行
$ /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"
- 安装sdl2
$ brew install sdl2 sdl2_image sdl2_mixer sdl2_net sdl2_ttf
- 查看下sdl2库安装情况
$ brew info sdl2
根据安装的版本不同,可能得到不同的信息,下面是我的
sdl2: stable 2.0.16 (bottled), HEAD
Low-level access to audio, keyboard, mouse, joystick, and graphics
https://www.libsdl.org/
/usr/local/Cellar/sdl2/2.0.16 (91 files, 5.4MB) *
Poured from bottle on 2021-08-27 at 05:51:01
From: https://mirrors.ustc.edu.cn/homebrew-core.git/Formula/sdl2.rb
License: Zlib
==> Options
--HEAD
Install HEAD version
==> Analytics
install: 107,393 (30 days), 219,217 (90 days), 957,010 (365 days)
install-on-request: 10,041 (30 days), 19,802 (90 days), 91,778 (365 days)
build-error: 0 (30 days)
下载SDL2-CS库
上面装的是sdl2库本尊,下面我们需要安装调用其功能的C#库。
SDL2-CS
是一个c#中SDL2库的一个封装,直接用nuget下载这个库是可行的,不过咱们这里直接引入源文件:
接着上文,我们现在处于项目根目录中
$ mkdir sdl
$ cd sdl
用wget下载文件
这里你直接把SDL2-CS项目中的
src
目录下文件下载到我们根目录也是可以的
$ wget https://github.com/flibitijibibo/SDL2-CS/raw/master/src/SDL2.cs
$ wget https://github.com/flibitijibibo/SDL2-CS/raw/master/src/SDL2_image.cs
$ wget https://github.com/flibitijibibo/SDL2-CS/raw/master/src/SDL2_mixer.cs
$ wget https://github.com/flibitijibibo/SDL2-CS/raw/master/src/SDL2_ttf.cs
$ wget https://github.com/flibitijibibo/SDL2-CS/raw/master/src/LPUtf8StrMarshaler.cs
回到项目根目录,尝试着运行一下:
$ cd ../
$ dotnet run
收到报错不安全代码只会在使用 /unsafe 编译的情况下出现
启用unsafe
上面的报错,咱们启用unsafe就好了。
编辑SDL2.csproj
文件,增加下面的文本
SDL2.csproj
这个文件,你用rider默认看不到的,简单点就用vscode或者任意编译器打开项目,就能看到这个文件了
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
这样以来文件内容就变成了
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
</Project>
项目转为dotcore
我个人习惯用dotcore开发,所以可以修改上面的SDL2.csproj
文件
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
</Project>
编辑Program.cs
内容修改为
using System;
using SDL2;
namespace SDL2_TEST
{
class Program
{
static void Main(string[] args)
{
SDL.SDL_Init(SDL.SDL_INIT_VIDEO);
SDL.SDL_Quit();
}
}
}
运行一下
$ dotnet run
看到报错,说是不存在SDL2.dll
Unhandled exception. System.DllNotFoundException: Unable to load shared library 'SDL2.dll'
下载dll
于是咱们从官网下载了SDL2.dll
,并且放到了根目录,还把属性设置为始终复制
,然而,还是没用。
于是求助了万能的谷歌,到官网找到了这样的一个帖子how-to-setup-sdl-with-c-bindings-in-visual-studio-for-mac,
文字内容很多,咱们挑简要的说,意思就是 :
- 咱们在
mac
下用的netcore 3.1
项目用的dll不应该是SDL2.dll
,应该是SDL2.dylib
那么SDL2.dylib
在哪儿呢?
寻找SDL2.dylib
这个文件是存在的,只不过不叫这个名字。
我们去这里找:
$ cd /usr/local/lib
$ open .
找到libSDL2.dylib
, 右键,显示原身,这文件就是我们要的。
复制到根目录
我们直接在原位置修改名字不合适,我们可以复制一份到项目根目录再修改:
- 将上面的文件复制到项目的根目录,然后设置属性为
始终复制
再次尝试运行,发现还是失败?!
$ dotnet run
同时寻找dll和dylib
这次不卖官子了,直接查看源代码sdl/SDL2.cs
发现这么一行
private const string nativeLibName = "SDL2.dll";
写死了寻找dll
文件,我们可以去掉这个dll
后缀,这样既可以寻找dll
文件,也可以寻找dylib
了。
我们改成
private const string nativeLibName = "SDL2";
再次运行,成了!
$ dotnet run
但是什么窗口都没看到,不行,必须看到窗口才算结束!
我们要看到窗口!
别说了,把Program.cs内容换成这个,再次运行就看到窗口了
using System;
using SDL2;
namespace SDL2_TEST
{
class Program
{
static void Main(string[] args)
{
SDL.SDL_Init(SDL.SDL_INIT_VIDEO);
IntPtr window = SDL.SDL_CreateWindow("测试窗口",
SDL.SDL_WINDOWPOS_CENTERED,
SDL.SDL_WINDOWPOS_CENTERED,
640,
320,
SDL.SDL_WindowFlags.SDL_WINDOW_RESIZABLE);
IntPtr renderer = SDL.SDL_CreateRenderer(window, -1, 0);
SDL.SDL_RenderSetScale(renderer, 10.0f, 10.0f); // Render a pixels 10 times bigger than they actually actually are
SDL.SDL_SetRenderDrawColor(renderer, 0, 0, 0, 0);
SDL.SDL_RenderClear(renderer);
SDL.SDL_SetRenderDrawColor(renderer, 255, 0, 0, 255);
for (int x = 0; x < 64; x++)
{
SDL.SDL_RenderDrawPoint(renderer, x, x / 2);
}
SDL.SDL_RenderPresent(renderer);
// "Game Loop"
SDL.SDL_Event e;
bool quit = false;
while (!quit)
{
while (SDL.SDL_PollEvent(out e) != 0)
{
switch (e.type)
{
case SDL.SDL_EventType.SDL_QUIT:
quit = true;
break;
case SDL.SDL_EventType.SDL_KEYDOWN:
switch (e.key.keysym.sym)
{
case SDL.SDL_Keycode.SDLK_q:
quit = true;
break;
}
break;
}
}
}
SDL.SDL_DestroyRenderer(renderer);
SDL.SDL_DestroyWindow(window);
SDL.SDL_Quit();
}
}
}
这次是真的成了!
最后一点优化
把所有类库放在根目录着实有点不雅,我们要做优雅的程序员,要学会目录管理,于是,我们新建目录lib
,把相关的dll
和dylib
都放到里面去。
项目一样可以运行!目录还合理一些了,何乐而不为?
最后项目目录在Rider中如下所示:
- 最上面的
SDL
名字可以无视,不影响运行的,如果按照我的步骤做,你可能叫做SDL2_TEST
- 我多了个
README.md
文件,这是我自己建的,因为我凡事喜欢做笔记...
留个尾巴,自我拓展
这个例子只用到了sdl2
核心库,但是sdl2_img
这些扩展库还没用到,到时候再遇到报错,相信你也可以举一反三,一一解决吧?