💡

D言語でeasy-to-useなゲーム用ライブラリraylibを試してみる

2021/11/07に公開

raylibとは

知ってる人は知っている、ベリーイージーな感じで使えると噂のOSSなゲーム開発用ライブラリです。
GoogleやらEpic Gamesからも表彰されてるようなゲームがraylibで作られているほか、最近はWebAssemblyにも対応していたりと開発が活発なことでも知られています。

raylib公式サイト

SDLとの比較などはまったく触れませんが、そんなベリーイージーなraylibをD言語からサクッと使う手順をまとめ、ウルトラベリーイージーな感じを体感してみます。

準備

今回raylibを使うためのライブラリは bindbc-raylib3 を使います。バージョンは 2021/11/07 現在 0.4.0 です。

なおraylibを扱うライブラリは現時点で6個登録されており、どれも割と更新されていて甲乙つけがたいのが現実です。
そんな中でもbindbcというメジャーシリーズの名を冠していて @nogc nothrow あたりのアノテーションがしっかりあり、動的リンクも静的リンクも対応しているこちらを選んだ次第です。

その他、現在raylibの最新版は 4.0.0 なのですが、元々同じ作者による bindbc-raylib という3がつかないライブラリもある ところ、なぜかraylib3の方を先に raylib 4.0.0 に対応してしまったので、せっかくだからこっちを使います。
とはいえロード部分のインターフェースも同じで、違いといえば対応raylibの既定のバージョンくらいです。やること変わらないのでほとんど好みの問題ですね。

さて、今回も今回でWindowsなので、DLLを拾ってきて実行ファイルの横に置いておくスタイルでいきます。Linuxでもコードは変わりません。

プロジェクトに参照を追加する

適当に sandbox-raylib というディレクトリを作り、そこをカレントにして dub init -n -f sdl でD言語のプロジェクトを作ります。

dub.sdl などのファイルができたら、追加で dub add bindbc-raylib3 を実行してライブラリの参照追加は終わりです。あとはDLLを取ってきて置きましょう。

DLLを取ってくる

Windowsの場合、例によってここが一番の難関です。使うライブラリが既定とするバージョンが raylib 4.0.0 なので、公式サイトからこれに沿ったDLLを取ってくる必要があります。

今回のライブラリは当然他のバージョンも対応していて、読み込むバージョンは version フラグで変えられますが割愛します。

では、GitHubを使い慣れている方々はraylibのReleaseページから取ってきてください!(丸投げ)
以下その手順を少し解説します。

  1. 「raylib」でネットを検索すると公式サイトが見つかります。右上のアイコンからGitHubのページに移動します。

(公式はドット感が強いですが、3D描画もシェーダーも使える普通のゲームエンジンです)

  1. 画像はダークモードですが、以下のようなページが表示されます。トップページ右のところにReleasesというコーナーがあり、ここにバージョン 4.0.0 のリリースページに飛ぶリンクがあります。

  1. 表示するとリリースの説明と共に様々なダウンロードするリンクがあるので、ここからWindowsの64bit用のファイルをDLしてきます。 mingwmsvc という2つがありますが、多くの場合は性能的に msvc がおすすめです。今回もmsvcの方をDLします。

raylib-4.0.0_win64_msvc16.zip が目的のファイルです。

  1. 展開するとファイルが lib ディレクトリがあり、中に raylib.dll など3つのファイルがあります。これをプロジェクトフォルダの dub.sdl があるところにコピーします。

コピー元

コピー結果

これで準備は完了です。後はコードを書いてビルドして実行すればお楽しみタイムです。

実装

まずはウィンドウを表示して適当に文字列を描画する公式サンプルです。わざとなのか何なのか、ひじょーに見づらい色合いなので背景色は変えました。

import std.stdio;
import bindbc.raylib;

void main(string[] args)
{
	RaylibSupport retVal = loadRaylib("raylib.dll");
	if (retVal != raylibSupport)
	{
		writeln("ERROR: ", retVal);
	}
	else
	{
		writeln("VERSION: ", retVal);
		writeln("loaded : ", loadedRaylibVersion);

		enum SCREEN_WIDTH = 800;
		enum SCREEN_HEIGHT = 450;

		// Initialization
		InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "raylib [core] example - basic window");

		SetTargetFPS(60); // Set our game to run at 60 frames-per-second

		// Main game loop
		while (!WindowShouldClose())
		{
			// Draw
			BeginDrawing();
			ClearBackground(DARKGRAY);
			DrawText("Congrats! You created your first window!", 190, 200, 20, LIGHTGRAY);
			EndDrawing();
		}
		CloseWindow();
	}
}

実行コマンド

dub run

実行結果

InitWindow でウィンドウを初期化し、FPSを設定したらゲームループ、という非常にシンプルな構造です。キーボード操作など入れ始めると少し形は変わるかもしれませんが、1つ1つの関数がかなり高級なので、色々やっても結構手数は少なく済むと思います。

困ったら サンプル実装 を見たり チートシート を見るのがおすすめです。

ちなみにサンプル実装はほとんどがC言語で書かれているので、場合によってはコピペで動きます。

画像を表示する

ゲーム開発のためのライブラリなので、3次元描画はもちろんキーボード操作やオーディオも対応しています。
ここまで来たら簡単な画像の表示くらいはやっておきたいですね。

raylibでは LoadTexture という関数があり、ファイルパスを渡すことで描画に使えるテクスチャ構造体が返ってくる仕組みとなっています。
なおこの LoadTexture 関数ですが、 InitWindow より後 にしないと内部の初期化の順序の問題でエラーが起きるので注意してください。(これを初手で踏み抜く豪運よ)

テクスチャ表示のサンプル漁っていて気付いたのですが、以下のようなコメントをしているところがありました。

// NOTE: Textures MUST be loaded after Window initialization (OpenGL context is required)
(意訳) テクスチャは 必ず ウィンドウ初期化のあとにロードする必要があります。OpenGLコンテキストが必要です。

あとは描画のループの中で DrawTexture 関数を使えば描画できます。

つまり実装をこうして…

import std.stdio;
import bindbc.raylib;

void main(string[] args)
{
	RaylibSupport retVal = loadRaylib("raylib.dll");
	if (retVal != raylibSupport)
	{
		writeln("ERROR: ", retVal);
		return;
	}

	writeln("VERSION: ", retVal);
	writeln("loaded : ", loadedRaylibVersion);

	enum SCREEN_WIDTH = 500;
	enum SCREEN_HEIGHT = 750;

	// Initialization
	InitWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "raylib [core] example - basic window");

	auto texture = LoadTexture("dman.png");
	scope (exit)
		UnloadTexture(texture);

	SetTargetFPS(60); // Set our game to run at 60 frames-per-second

	// Main game loop
	while (!WindowShouldClose())
	{
		// Draw
		BeginDrawing();
		ClearBackground(WHITE);
		DrawTexture(texture, SCREEN_WIDTH / 2 - texture.width / 2, SCREEN_HEIGHT / 2 - texture.height / 2, WHITE);

		enum message = "Congrats! You created your first D-man!";
		const messageSize = MeasureText(message, 20);
		DrawText(message, SCREEN_WIDTH / 2 - messageSize / 2, SCREEN_HEIGHT / 2 + texture.height / 2 + 30, 20, RED);
		EndDrawing();
	}
	CloseWindow();
}

こうじゃ!!

画像参考 : Qiita - HTMLとCSSだけで描く簡単D言語くん(描き方解説つき)
- https://qiita.com/lempiji/items/dd44003481739e3101de

まとめ

と、raylibを使うのは思ったよりも簡単でした。これは確かにイージーです。
一時期4.0.0のバイナリどこー?と探していた気もしますが、何日か寝て起きてしてたら普通にありました。時間が解決してくれることもあります。

あと複数ライブラリが乱立していた場合、bindbcとついているのがおすすめですが、作者や機能などいろいろな面で比較してみるのが大切そうですね。

そういえばQiitaのアドベントカレンダーが近づいてきています。
私は毎年こうしてネタ整理していると気付いたら放出してしまう性分なので、みなさんはぜひアドベントカレンダーをきっかけに記事を書いてみたり、本番に向けてしっかりスケジュール調整していってください!

おわり。

Discussion