Cygamesのゲームエンジン開発

多胡順司氏(以下、多胡):みなさま、暑い中お集まりいただきありがとうございます。本セッションでは「Cygamesにおける次世代ハイエンドコンソール向けゲームエンジンの開発」と題しまして、私、多胡が発表させていただきます。

最初に、簡単なアンケートを取りたいと思います。今現在、商用エンジンを使ってゲームを開発しているという方はどのぐらいいらっしゃいますか?

(会場挙手)

自社でエンジンを開発、自社エンジンを使っているという方はどのぐらいいらっしゃいます?

(会場挙手)

けっこう多いですね。商用エンジンを使っているけど、エンジンを作るのに興味があるという方、どのぐらいいらっしゃいますか?

(会場挙手)

まあまあいますね。その方の顔をよく覚えておきます。

(会場笑)

本セッションのサマリーですが、Cyllista Game EngineというCygamesで作っているゲームエンジンを軽く紹介した後、そのエンジンの開発において大きな目的としている、ゲーム開発者がイテレーションを速く回せるようにするための取り組みについて説明させていただきます。

私は、Cygamesで技術本部でCyllista Game Engineの統括マネージャーをしています、多胡と申します。よろしくお願いします。

Cyllista Game Engineとは何か?

Cyllista Game Engineと言いましても、たぶん誰もまだ見たことないと思います。ですので、簡単なデモムービーを用意してきましたのでご覧ください。ゲーム画面っぽいものが映るのですが、これは今日のために僕が1日で作ったステージなので、Cygamesで開発中のゲームとは関係ありません。その点をご了承ください。

(画面を指して)これがメインのレベルエディタのウィンドウになっています。エディタからWindows版のゲームコンソールを起動したり、PS4版を起動したりできます。左下に映っているのがPS4の画面です。ステージを選択して開いて、Windows版のゲームをプレイしています。FPSのゲームで、PS4も同じようにプレイできます。

レベルエディタなのでエディットができます。エディタのカメラがWindows版とPS4版で同期して、WindowsとPS4で同じものが見えるようになっています。アセットの配置をしたりとか、「ちょっときのこが足りないな」と思ったらきのこを増やしたりとか、比較的手軽にレベルの編集ができるツールになっています。

編集結果はそのままゲームに即反映されて、ゲームプレイ中でも編集が可能になっています。

Cyllista Game Engineは、Cygamesの内製のゲームエンジンで、フルスクラッチでゼロからコードを作成しています。ランタイムだけでなく、ゲームの開発環境も含めたツール類をすべて提供する統合型のゲームエンジンとなっています。PS4などに代表される、ハイエンドコンソール向けのAAAタイトルを作るために開発しているゲームエンジンです。

目標は「最高のゲームエンジンをつくること」

このCyllista Game Engineの目標はただ1つ、簡単ですが、最高のゲームエンジンを作ることです。

最高のゲームエンジンと言ってもちょっとふわっとしていてよくわからないので、もう少し噛み砕いて説明しますと、まず1個目が「ハードウェアが最高のパフォーマンスを出せる」という点ですね。そしてもう1つは「ゲーム開発者が最高のパフォーマンスを出せる」。この2つの柱をゲームエンジンの目標としています。

「ハードウェアが最高のパフォーマンスを出せる」とはどういうことかというと、そのままですがハードウェアの性能を最大限に引き出すことです。ジョブシステムやデータ指向設計などをきっちりやっていきます。

「ゲーム開発者が最高のパフォーマンスを出せる」ということなのですが、これは一言で言うと、イテレーションをいかに速く回すかにかかっています。

今、ゲームの開発はイテレーションをいかに速く回せるかにかかっていて、ゲーム開発者の待ち時間をとにかく減らすことを目標にやっています。ゲーム開発者の作業結果が即時にリアルタイムで確認できることを主眼においています。

では、「ゲーム開発者が最高のパフォーマンスを出せる」という、この「ゲーム開発者」とは誰なのかといいますと、もちろんゲームコンテンツ制作者です。ゲームデザイナーだったり、レベルデザイナーだったり、アーティスト、サウンドデザイナー、QA(品質保証)、ゲームエンジニアです。

そしてもちろん忘れてはいけないのが、我々エンジン開発者です。

エンジン開発者が最高のパフォーマンスを出せると、当然ハードウェアが最高のパフォーマンスを出せることになりますので、今回は右側の「ゲーム開発者が最高のパフォーマンスを出せる」という話が中心になります。

ゲームエンジンは機能ごとにモジュール化

ゲーム開発者をここで大きく3つに分類して、エンジン開発者とゲームプログラム開発者、ゲームコンテンツ開発者に分けて、それぞれの開発者向けに話していきます。

まずはエンジン開発者向けの話をしていきます。最初はモジュール化の話です。

ゲームエンジンは機能ごとにモジュールに分けて作っています。モジュールはさらにレイヤーの概念を持っていて、下のものほど低レベルなレイヤーになっています。下のほうがハードウェアに近くて、上になるほど抽象度が上がる感じですね。

この図の例だと、右下の青い部分がエンジンのランタイムで、その上にアプリケーションコード、ゲーム固有のコードが乗るという構成になります。モジュールの依存は下向きの一方向で、相互依存は不可にします。モジュールごとにDLL化して開発をしています。

モジュール化の利点ですが、エンジンのユーザーの視点から見ると、必要モジュールだけを取り出して利用可能になる点です。

エンジン開発者の目線から見ると、ビルド時間の短縮が大きなメリットです。依存しているモジュールだけで開発が可能になるので、コンパイル時間が短くなることですね。

さらに開発しているモジュールだけ、例えば1つ前の例でいうと、Gfxというモジュールがあると思いますが、ここを開発するにはGfxとCoreだけがあればよく、Gfxを開発している間はGfxだけがリンク対象になるので、ビルド時間を短縮できます。

モジュールに分割した上で単体のモジュールをどのように開発していくかといいますと、テスト駆動開発をやっています。モジュールが公開しているAPIに関して、テストを記述しながら開発を進めていくことを徹底してやっています。

テスト駆動環境の利点は「安心感」

続いてテスト駆動開発について簡単に説明していきますと、テスト駆動開発は一般的な進め方でやっています。テストコードを書く、テストが失敗するように実装して、テストが通る最小限の実装を記述するという、この3ステップを繰り返してコードを書いていきます。

具体的に見ていくと、(スライドを指して)左側がテストコードで、右側がヘッダーとcppファイル。最初はなにもないですが、最初にテストコードを書きます。システムをGetして、それがnullじゃないというテストを書きます。

続いてテストが失敗する実装を書くので、nullを返す実装を書いて、テストが失敗することを確認し、テストが通る最低限の実装を書いていくということをやります。

nullを返していたのを、nullじゃないなにかを返すコードにするだけですね。MySystemImplというのを作って、中身はなにも書かずにそのポイントを返すだけ、みたいな感じにします。

テストが通ることを確認したら、新たなテストを書きます。次に、新しい関数としてcreateObjectという関数を作って、このオブジェクトがnullじゃないと確認します。そしてまた失敗する実装を書くので、nullを返す実装を書きます。また続いてテストが通る最低限の実装を書くので、なにもしないオブジェクトを返してあげるということをやる。

これを繰り返していくことでAPIをテストするコードを開発していくことをやっています。

このテスト駆動開発の利点は、1つは当たり前ですがコードが安定します。実際テストはJenkinsなどのCIで常時実行しているので、APIが動かなくなっているのが即座にわかるようになっています。

また副次的なメリットとして、まずテストを書いているので、おのずとユーザー視点でAPIの設計ができるという利点があります。実装側から書いてしまうとどうしてもAPIのデザインが実装に寄ってしまうことがあるのですが、書く側の視点でAPIを設計していくことになるので、API自体の質が高いものになります。

またAPI自体はずっとテストされ続けているので、リファクタリングをするときの障壁が下がります。リファクタリングをすることにより、さらにコードが安定します。

簡単にテスト駆動開発の利点を一言でいうと、「安心感」ですね。なによりもテストが動いていれば、自分が書いたコードは間違いなく動いていることで、なにかAPIが動かなくなったときにすぐ気づくことができるので、安心してコードができるのが一番のメリットですね。

テストコード書かない問題への回答

そのテスト駆動開発ですが、問題点がありまして。実際にやってみるとテスト駆動開発はすばらしいのですけど、導入するのが難しいという話をよく聞きます。

なぜか? 例えば、製品に乗らないテストコードを記述するのは手間なので、「めんどうくさい!」とか「テストコードを書く意味があるの?」といった声があがってきます。「じゃあ、どうしたら導入ができるのだろう?」と考えてみた結果、すばらしいアイデアを思いつきました。

「テスト駆動開発以外の開発方法を提供しない」。

(会場笑)

そうすればみんなテスト駆動開発をしてくれることに気づいたのですね。なので、Cyllista Game Engineでは、エンジンのモジュールはテスト以外では開発できないようになっています。

とはいえ、厳密には自分でアプリケーション側のコードを一生懸命書いてコードを組んでいけば開発は可能ですが、テスト駆動開発のほうがはるかに楽であると思ってもらう取り組みをしています。

具体的にどうするかというと、エンジン開発用のツールを用意して、テスト駆動開発の敷居をとにかく下げることをやっています。

エンジン開発ツールをCUIにした理由

そのエンジン開発ツールはどういったものかを説明させていただきますと、cybuildと呼んでいるのですが、これがエンジン開発で唯一利用するツールですね。

エンジンで必要なことはなんでもできるすばらしいツールなのですが、その見た目をここでお披露目したいと思います。

CUIです。

(会場笑)

このコマンドプロンプトに「cybuild」って打てばいいだけですね。

そんなcybuildですが、やることはたくさんあるので全部コマンドになっています。例えば普段の開発のサイクルを見ていくと、「cybuild update」というコマンドでソース・コード・アセットの更新をかけて最新版を取ってくることができて、「cybuild test」というコマンドでモジュールのテストが動かせる。

開発コードを書き終わったら、「cybuild push」でソースコードとかツールとかアセット、関連するツール・アセットのサブミットができるかたちになっています。

ではこのツールを使って実際にどのようにテスト駆動開発をやっていくかというと、コマンドを打っていくだけです。Gfxモジュールのビルドとテストの実行をしようと思ったら、こんなコマンドを打てばいい。

PS4版をテストしようと思ったら、プラットフォームオプションに「ps4」とつければいい感じですね。

あと、ソリューションファイルをエクスプローラーで探して開くのがめんどうなので、ソリューションファイルを開くコマンドも用意していたりします。

そのCUIツールのデモムービーを用意してきたので、ぜひ見てください。「cybuild test Gfx premake build run」と打つと……あの、見ちゃいけない部分はモザイクかけているので大丈夫です。

(会場笑)

グラフィックスのテストはスクリーンショットを撮って画像の比較をするようになっています。

そのcybuildの主な機能ですが、モジュールのテストはもちろんのこと、ドキュメントの生成やエディタの起動、クリーンの処理、ファイルの変更状態や、ファイルのステータスの取得など、なんでもできるようになっています。

CUIツールをただ1つだけ用意することの利点のまず1つめは、すべてのツールが1つになっているので迷わない。なにをするにしても1個のコマンドを覚えればいいということがあります。覚えることはただ1つですね。

もう1つは、ツールのヘルプがついている。これは“-h”オプションで全部出るようになっていて、コマンドの一覧も取れるので、なにができるのかが一発でわかるようになっています。

またCUIなので、JenkinsとかのCIでの実行が簡単ですね。そのまま取り込むだけなので大した手間がなく取り込むことができるようになっています。

Cyllista Game Engineで使用できる言語

続きまして、また別の取り組みについて話しますと、Cyllista Game Engineではプログラミング言語を統一しています。あまり多くのプログラミング言語を使わないようにしています。

まず1個はもちろんC++ですね。エンジンのランタイム自体をC++で書いていて、エンジンのランタイム以外でも速度の要求される場面、アセットの変換処理が重いところなどはC++で書くみたいなことをやっています。

それ以外のあらゆるものはPythonで書いています。最初にお見せしたレベルエディタも、先ほど紹介したcybuildもPythonで書いています。アセット変換処理も、重くないものはPythonで書くことをやっています。

「なんでPythonなの?」というところですが、とにかくモジュールが充実していることが理由です。Pythonのパッケージのインデックスに登録されているのが11万件、Rubyのgemが8,000ぐらいです。とにかくなにかしようと思ったらほとんど場合モジュールが存在するので、Pythonを選んでいます。

これはPythonというよりはスクリプト言語を使っているからですが、ツールの配布が簡単です。Version Control Systems(Perforce)にPythonのソースコードをサブミットするだけでツールが配布できて、使う側はsyncすれば使えるようになります。ビルドの手間がないのが大きいですね。

あとはコードの共有化が簡単であるのも大きな利点です。内部で作っているコードはすべてモジュール化しているので、インポートすればどこからでも利用がすぐできるようになる点は大きな利点となっています。

もちろんPythonも……どちらかというとPythonこそテスト駆動開発をする必要があって、こんな感じでユニットテストを書いて、テスト駆動開発もモジュールごとにテストを動かして開発をしています。

インデントのタブ or スペース問題がついに決着

続きまして、また別の話題に移るのですが、コードの自動整形について話します。エンジンのソースコードはC++とPythonで書いていると話しましたが、C++はclang-formatで自動整形して、Pythonはpep8とflakeで自動整形しています。エンジンで開発しているほぼすべてのコードは基本的に自動整形をしています。

この自動整形の最大のメリットはなにかというと、これです。インデントのタブ/スペース論争がついに終焉しました!

(会場笑)

人類はこの論争にいかに時間を無駄に浪費してきたかということなのですけど。問答無用で整形されるので、タブだろうがスペースだろうが好きなほうを使ってくれという感じですね。最終的には勝手に統一してしまいます。

なにより自動整形する利点は、コードを書くときも読むときもコードの中身に集中できることです。書く側では「コーディング規約って、if文のあとのカッコにスペース入れるのだっけ?」みたいなことを一切考えなくてすみます。

読む側にとっても、見るところによってコードの書き方が違うということもなくなりますし、レビューするときにいちいちコーディング規約について突っ込まなくて済むようになります。これが一番大きいメリットですね。