かくことない

かもしれない

Vcpkg+CMakeでのSDL2ビルド

C++用パッケージマネージャVcpkgと C系言語用ビルドソフトCMakeと組み合わせてSDL2をビルドした。

環境

この記事の内容はWIndows10でなくてもおおむね問題なく使えるはず。 なお、CMakeは既に導入済みとして話を進める。

Vcpkgの導入

READMEに従い導入を進める。

Vcpkgのレポジトリをcloneし、中にあるbootstrap-vcpkg.bat(Linux等ではbootstrap-vcpkg.sh)を実行する。

> git clone https://github.com/Microsoft/vcpkg.git
> cd vcpkg
> .\bootstrap-vcpkg.bat

Vcpkgとビルドシステムを連携するために、integrate installコマンドを実行する(管理者権限が必要)。

> .\vcpkg integrate install

尚、必要に応じてPATHを通してもよい(自分は面倒で通してない :P)。

SDL2の導入

vcpkg installを使用する。

> .\vcpkg install sdl2 --triplet x64-windows

--tripletオプションの引数は環境によって変える。 有効な引数は.\vcpkg help tripletから見れる(後々変わるかもしれないので、一応自分で実行したほうがいいかもしれない)。

> .\vcpkg help triplet
Available architecture triplets:
  arm-uwp
  arm-windows
  arm64-uwp
  arm64-windows
  x64-linux
  x64-osx
  x64-uwp
  x64-windows
  x64-windows-static
  x86-uwp
  x86-windows
  x86-windows-static

ビルド

最初に、CMakeLists.txtを書く。 vcpkg install完了時に表示されるスニペットを参考にする。

project(main)

find_package(sdl2 CONFIG REQUIRED)

add_executable(main main.cpp)
target_link_libraries(main PRIVATE SDL2::SDL2 SDL2::SDL2main)

次にcmakeを実行し、ビルドスクリプトを作成する。 今回はCMakeLists.txtを同じディレクトリで実行したとして書く。 実際にはbuildとかそういうビルド用のディレクトリを作ってそこでやった方がいいと思う。

> cmake . "-DCMAKE_TOOLCHAIN_FILE=<vcpkg_root>\scripts\buildsystems\vcpkg.cmake" -A x64

<vcpkg_root>にはVcpkgがあるディレクトリを指定する。

-A x64は使用するビルドジェネレータやビルド対象とするアーキテクチャによって変えること。

例えば、Ninjaで64ビットのWindowsを対象にビルドする場合は

> cmake . "-DCMAKE_TOOLCHAIN_FILE=<vcpkg_root>\scripts\buildsystems\vcpkg.cmake" -G Ninja -DVCPKG_TARGET_TRIPLET=x64-windows

となる。

最後に、cmake --buildでビルドする。

> cmake --build .

ここで指定するディレクトリは先程ビルドスクリプトを作成したディレクトリであることに注意しよう。 メインのCMakeLists.txtがある場所とは限らない。

リンクエラーが出たら

私の場合、リンクエラー(LNK2019)が出てしまいビルドが成功しなかった。

理由はSDL2はmainにマクロを入れており、 コマンドライン引数を明示しないとそのマクロが動作せずリンクエラーとなる(参考)。

int main() // ダメ!
int main(int argc, char* argv[]) // よし!

OmegaTで既存の翻訳文章を参考訳文として再利用する方法

某アプリの日本語訳が微妙だったので直そうと思ったのだが、OmegaTでその訳を入力する方法が分からなかったのでメモ

  1. OmegaT4.xをインストールする
  2. この機能は3.xにも存在するようだが、GUIのサポートが無いので4.xがおすすめ。

    3.xでやる方法はこちら

  3. [ツール]→[ファイルの整列]を選択
  4. 訳文に取り込みたい訳のファイルを指定し確定
  5. ちゃんと対応が取れているか確認して次へ
  6. 必要に応じて比較モードやアルゴリズムを調整する。 私の場合は翻訳対象がAndroidのリソースファイルだったのでIDで比較するだけで完璧になった。

  7. 同様に確認してTMX(翻訳メモリファイル)を保存
  8. 必要に応じて調整する。

    保存後は再読み込み(F5)すれば参考訳文に元の訳が表示される。 参考訳文または選択範囲に置換(Ctrl+R)を使えばそのまま利用できる。

    参考

    New interactive aligner in OmegaT

8時間でゲームを作れなかった話

Ludum Dareというゲームジャムがあります。ゲームジャムというのは一定の時間でゲームを作る(要するにゲーム版ハッカソン)というもので、Ludum Dareはその中でも世界最大規模のものです。

自分は次回のLudum Dareに参加しようと思っているのですが、ゲームジャムに参加したことがありませんでした。というわけで、昨日(2019/6/8)試しに8時間で一人ゲームジャムをやってみました。

できたやつのソースコード(github)

ルール

  • 時間は8時間。ただし、ごはん休憩とお風呂休憩中は時間をすすめない
  • テーマはランダム単語ジェネレータで開始時に決める
  • 用意してよいのは開発環境とコードのテンプレートのみ。絵などが必要であれば時間中に作成する。素材の使用は認めない。
  • 泣かない

ゲームジャム中の風景

警告: 無編集、約9時間

シークバーの真ん中あたりをクリックして「へー」と思ってくれたら十分です。

Youtube

できたものの説明

テーマ「まつぼっくり、戦艦、ボーダーライン」にそって考えた結果、まつぼっくりを広い範囲に届けて生存範囲を広げるゲームになりました。

ゲームが始まると、松の木からまつぼっくりが落ちてきます。右クリックを押している間まつぼっくりの傘が開いて(画像1枚しかないから見た目は開いてないけど)右方向に進むことができます。 地面に着地するまでに進めば進むほど、生存範囲を広げたということで高得点がもらえます。 ただし、着地したまつぼっくりを何故か狙う鳥がいます。この鳥にまつぼっくりをとられてしまうと、その分の点数が減ります。

まつぼっくりは何故か謎の弾丸を発射することができるので、この鳥を打ち落とすことで点数の減少を防ぐとともに、鳥が持って行った分だけ点数アップを狙うことができます。

なお、弾丸の発射と傘を開くことを同時に行うことはできません。バランスが重要。

f:id:ix5231:20190609203011p:plain

反省点

完成しなかった

結局、8時間以内に完成させることができませんでした(レポジトリのコードは時間が終わった後に調整したもの。ゲームジャム終了時点のコードが見たい場合はgame-jam-endタグを参照)。

おそらく、ペース配分をするときに休憩を計算に入れなかったことが原因の一つだと思います。例えば、1時間ごとに5分休憩をとるなどを決めていれば、途中で必要以上に休憩したりすることがなかったと思います。 また、ここで私が言う「完成」は「ゲームが開始し、プレイ結果が出て、終了する」、この一連の流れができることを言っています。今回の場合、ゲーム終了の実装が間に合いませんでした(つまり、ゲームが永遠に終わらない)。 個人的に、そもそも先にゲームの終了部分を作るべきでだったと思います。 ゲーム本体がまだまだ未熟でも、とりあえず結果が出て終了すればゲームと言い張れると思うからです。

使ったテンプレートやライブラリの把握が足りなかった

今回、開発にはRust+ggezを選択し、ggezの開発者の方が作ったテンプレートを基にゲームを作りました(ggezは結構低レベルなライブラリで一からやると結構めんどい)。Rustはともかくとして、ggezに関しては何か月か前に少し触って以来だったので、APIの把握があまりできていませんでした。 また、テンプレートの方も前日に読んで動かしただけで、ちょっとコードを書いてみて弱点等の把握をしていなかったため(実はマウスの対応が不完全だった等)、それらに若干振り回されてしまった時もありました。

感想

完走した感想ですが、意外と楽しかったです。自分はかなり飽きっぽいのぶっちゃけ30分くらいで飽きると思っていましたが、なんだかんだで完走できました。

今回は個人的嗜好によりRustで作りましたが、今度はUnityとかデカいゲームエンジンで作ってみたいです。C#言語4.0以来使ったことないけど。それともまさかのF#? 次回やるとした、多分9月くらいだと思います。次は時間内に完成させたいところ。

ggezをバージョンアップしたらnix-shell上でプログラムが起動しなくなった話

タイトルはggezだけど、エラーメッセージはglutinwinitから出ているからそれ関係なら使えるはず。

解決策

shell.nixに以下のような設定を加える。

LD_LIBRARY_PATH = lib.makeLibraryPath [
  "/run/opengl-driver"
  xorg.libX11
  xorg.libXcursor xorg.libXrandr xorg.libXi libGL
];

Rustなどその他もろもろも入れるとこうなる。

let
  moz_overlay = import (builtins.fetchTarball https://github.com/mozilla/nixpkgs-mozilla/archive/master.tar.gz);
  nixpkgs = import <nixpkgs> { overlays = [ moz_overlay ]; };
  rust = nixpkgs.rustChannels.stable.rust;
in
  with nixpkgs;
  mkShell {
    buildInputs = [
      rust alsaLib pkgconfig SDL2 libudev
    ];
    LD_LIBRARY_PATH = lib.makeLibraryPath [
      "/run/opengl-driver"
      xorg.libX11
      xorg.libXcursor xorg.libXrandr xorg.libXi libGL
    ];
  }

事の顛末

ggezを0.5.0-rc0にバージョンアップしたら、1 以下のエラーが出て自分のプログラムが動かなくなってしまった。

thread 'main' panicked at 'Failed to initialize any backend! Wayland status: NoCompositorListening X11 status: LibraryOpenError(OpenError { kind: Library, detail: "opening library failed (libXcursor.so.1: cannot open shared object file: No such file or directory); opening library failed (libXcursor.so: cannot open shared object file: No such file or directory)" })', /home/ix/.cargo/registry/src/github.com-1ecc6299db9ec823/winit-0.18.0/src/platform/linux/mod.rs:437:9
note: Run with `RUST_BACKTRACE=1` for a backtrace.

最初は依存関係にlibXcursorが足りないのかと思ったから、buildInputsxorg.libXcursorを追加してみたが改善しなかった。

ここでメッセージをちゃんと読むと動的ライブラリlibXcursorがないと出ている。 ということはLD_LIBRARY_PATHが通っていないと思い、試しにecho $LD_LIBRARY_PATHをしたところ案の定だった。2

$ echo $LD_LIBRARY_PATH
/run/opengl-driver/lib

そこでlibXcursorLD_LIBRARY_PATHへ追加した。

LD_LIBRARY_PATH = "${xorg.libXcursor}/lib";

上の「動的ライブラリがない」エラーがlibXrandrlibXiで出たから同様のことを繰り返す。

LD_LIBRARY_PATH = "${xorg.libXcursor}/lib:${xorg.libXrandr}/lib:${xorg.libXi}/lib";

これで直ったかと思いきや、次は以下のようなエラーが出た。

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: NotSupported("both libglx and libEGL not present")', libcore/result.rs:1009:5
note: Run with `RUST_BACKTRACE=1` for a backtra

libEGLは字面から見て関係なさそうだから、3あとのlibglxが問題なはず。

libglxはGLXの実装でOpenGLをX Window System上で使うためのものっぽい

何も考えずnix search libglxをしたところ何も見つからなかった。でもlibGLなるものはあったからこれを前のようにLD_LIBRARY_PATHに入れてみる。

LD_LIBRARY_PATH = "${xorg.libXcursor}/lib:${xorg.libXrandr}/lib:${xorg.libXi}/lib:${xorg.libGL}/lib";

そうすると次は以下のようなエラーが出た。

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: OsError("`glXQueryExtensionsString` found no glX extensions")', libcore/result.rs:1009:5
note: Run with `RUST_BACKTRACE=1` for a backtrace.

こんどはglX extensionsなるものがないらしい。

GLX extensionはグラフィックドライバによってサポートされるものらしいから試しにパソコンを変えてみたけれど変わらなかった。つまり、ドライバの問題ではないはず。

ここで、試しにglxgearsを起動してみることにした。glxgearsmesaのデモプログラムで、その名の通りGLXで歯車が描画される。

やってみたところ、エラーが出て起動しなかった。ただ、nix-shellを使っていない状態では問題なく起動できた。

ならばnix-shellを使っていない状態に何か手がかりがあるかもしれない。nix-shell抜きでLD_LIBRARY_PATHを確認してみる。

$ echo $LD_LIBRARY_PATH
/run/opengl-driver/lib

あ、なんかopenglとか書いてあるしそれっぽい……。

というわけで上で出てきたものをshell.nixに追加したらめでたく動いた。

LD_LIBRARY_PATH = "${xorg.libXcursor}/lib:${xorg.libXrandr}/lib:${xorg.libXi}/lib:${xorg.libGL}/lib:/run/opengl-driver/lib";

ただ、なんだか記述が野暮ったくて気に入らないので既存のパッケージを色々見てみたところ、lib.makeLibraryPathというそのまんまな関数があったので早速使う。4

LD_LIBRARY_PATH = lib.makeLibraryPath [
  "/run/opengl-driver"
  xorg.libXcursor xorg.libXrandr xorg.libXi libGL
];

冒頭のコードにはこれに加えてxorg.libX11がある。これはこの記事を書く前にもう一度動作確認してみたところ何故か足りないと言われてしまったので追加したから。理由は謎。


  1. これはテスト版だけど、0.4から多くの変更がAPIに加えられているらしいから移行の手間を省くためにあえてバージョンアップした

  2. ここ伏線

  3. OpenGL ESは組み込み用って書いてある

  4. Nixpkgs Manualにはこの関数のマニュアルが書いてなかったけど、当該関数のコードを見てみるとちゃんと書いてあった。ネットのマニュアルはどうやら自動生成のようなので、多分、次のリリース時にオンラインでも見れるようになるはず。