次世代型ゲームエンジン「Xenko」を触ってみた
次世代型ゲームエンジン「Xenko(ゼンコ)」とは、シリコンスタジオによって提供されているクロスプラットフォームゲームエンジンです。Xenkoを利用する開発チームに対して「すべてのゲーム開発者に、より早く・より自由にゲームを作れる環境を提供したい」という想いが軸にあるとのことです。公式ホームページを参照すると2017年7月31日までならばサブスクリプションプランの「Pro」が無料で利用できるので今のうちに試しておくのが良いでしょう。
1.Xenkoを手に入れる
公式サイトは日本語に対応していませんが、サブスクリプションプラン毎に利用できる機能が記述されている箇所に「Download」ボタンがあるので、お好みのプランを選択してダウンロードしましょう(Pro Plus、Customは問い合わせる必要があるようです)。
2.ランチャーからサンプルプロジェクトを作成
Xenkoのインストールが終わるとランチャーが起動できるので、画面左上にある赤いスタートボタンを押して新規サンプルプロジェクトを作成します。
下図の画面のようにいくつかのサンプルプロジェクトが初めから用意されているため、いくつかのサンプルを見るだけでXenkoでのゲーム開発方法を学ぶことができます。今回は「JumpyJet」というサンプルプロジェクトを例に作り方を見ていきたいと思います。
3.よく使うウィンドウについて
本章ではXenkoでのゲーム開発でよく使うウィンドウの簡単な紹介をします。
下図で示した場所に表示されているのは、現在のシーンに配置されているエンティティ一覧になります。アセットの種類によってはダブルクリックによってアセットの編集エディタを立ち上げることもできます。
下図で示した場所に表示されているのは、シーンに配置されているエンティティのプロパティになります。編集したいプロパティを持ったエンティティを選択した時に利用します。
下図のピンクの枠で示したソリューションエクスプローラー上の「Assets」を選択すると、青の枠で示したアセットビューにプロジェクト内で利用できるアセット一覧が表示されます。
下図のピンクの枠で示したソリューションエクスプローラー上の「JumpyJet.Game」を選択すると、青の枠で示したアセットビューにプロジェクト内で利用できるスクリプト一覧が表示されます。
4.ゲームの起動方法
ゲームを起動するためにはウィンドウ下図のピンクの枠で示した緑色の三角形のボタン(現在のプロジェクトのビルドとゲームの開始)を選択します。ショートカットキーも用意されており、「F5キー」が「 現在のプロジェクトのビルドとゲームの開始」、「F6キー」が「 現在のプロジェクトのビルド」となっています。
5.利用アセットの解説
5.1 UI制御
5.1.1 プロパティを増やす方法
「JumpyJet」ではボタンの制御とスコアの制御にスクリプトを利用しています。シーン上に配置されている「UI」エンティティを選択するとプロパティグリッドに「UI Script」コンポーネントが付けられているのが確認できます。「UI Script」を開いてみると、「public」として「Font」と「UIImages」が宣言されているのでプロパティグリッドで変数の値が編集できるのだと理解できます(Unity脳)。例えば、「public SpriteSheet TestImages;」と記述して保存すると「TestImages」という値をプロパティグリッドより編集できます。ちなみにですが、「[DataMemberIgnore]」を利用するとpublicとして宣言してもプロパティに表示されないようにすることができるようです。
5.1.2 スプライトシートを追加する方法
UIにはスプライトシートが利用されていますが、どのように「Assets」フォルダに追加するか説明します。今回は「JumpyJet」にサンプルが用意されているのでそちらを利用します。下図の「JumpyJet\JumpyJet\Resources\tex1.png」を「Assets」フォルダにドラッグアンドドロップして下さい。
ウィンドウが表示されるので「2D sprites」を選択します。これだけでスプライトシートの追加は完了です。
5.1.3 スプライトシートを利用する方法
もちろんですが、5.1.2章で追加したスプライトシートは設定しなければ期待通りに利用することができません。追加したスプライトシートをダブルクリックして開いてみます。下図の画面では追加したはずのテクスチャが表示されていません。
下図のピンクの枠で示した箇所に「Sprites」という項目がありますが、ここで1つ1つスプライトを選択して設定する必要があります。やり方としては、すでに追加されている「tex1」を「button」にリネームし、「button」に関連付けたいスプライトを選択します。「+」ボタンを押すことでスプライトを増やせます。
下図はキャラクターを選択している例ですが、スプライトを選択する時は「魔法の杖」を使います。
5.1.4 スライス設定
5.1.3章までの操作により、スクリプトでスプライトシートを利用するための設定が完了しました。さらにスプライトシート上でスライス設定もできるのでボタンスプライトのプロパティを編集してみます。
スライスを設定することでどんなメリットがあるのか下図に例を示します。ボタンに注目するとオリジナルの汚れが引き伸ばされているのと、引き伸ばされていないのが確認できます。このように引き伸ばされたくない部分の設定をするためにスライス設定が利用できます。
スライスの設定をするためには、スプライトのBordersパラメータによって伸ばしたくない範囲を指定することになります。それぞれX(Left)、Y(Top)、Z(Right)、W(Bottom)となっているようです。つまり、Xの値を100に設定すると汚れの部分が伸ばしたくない範囲から外れることになります。
5.1.5 UI Script
「UI Script」の処理の流れとしては、ゲーム起動時に「メイン画面」、「ゲーム画面」、「ゲームオーバ画面」のUIを作成しておきます。画面上に配置されているボタンが押されることで、予め作成しておいた画面UIを切り替える仕組みになっています。「ゲーム画面」にはボタンがありませんが、プレイヤーがゲームオーバになるとゲームオーバイベントが発行されるため、そのイベントを受信したタイミングで「ゲームオーバ画面」に切り替わります。
①スプライトシートで設定したスプライトを読み込みます
②下記のコードではボタンの設定とボタン押下時の処理が記述されています。Buttonクラスを見ると「Content」、「Padding」、「MinimumWidth」といったパラメータは用意されていないように思えますが、Buttonクラスが継承している「ContentControl」、「Control」、「UIElement」にパラメータが用意されています。全てを把握することは難しいので、やりたい事が出てきた時に調べる程度で良いと思います。
ボタン押下時の処理に「GameGlobals」というクラスが登場していますが、これはスクリプトとして「JumpyJet.Game」に追加されており、内容としては各種イベントの定義やパラメータ定義に利用しているようです。
メイン画面のUIの作成が全て終わった後に、「ModalElement」の「Content」にパーツを代入しています。「ModalElement」によって自身より下の入力(おそらくレイヤー的に)が得られなくなるので、入力制御に利用できそうです。今回は「HorizontalAlignment.Stretch」と「VerticalAlignment.Stretch」を指定しているので、親要素全範囲が埋まることになります。
③Update関数は毎フレーム実行され、ここでは常に「パイプをくぐり抜けたか?」、「ゲームオーバになったか?」という2つのイベントを確認しています。「TryReceive」はバッファから1つのイベントを受信するために利用され、イベントを受信したかどうかについてBOOLが返ります。「TryReceiveAll」というメソッドも用意されており、こちらはキューから全てのイベントを受信するとのこと。
5.2 パイプ制御
パイプとは「Touch to Start」ボタン押下後のゲーム画面で出現する障害物のことです。パイプは一定の間隔で出現しますが高さがランダムで変化します。その他、スクロール処理についても「PipesScript」に全てまとまっているため、このスクリプトを見ればパイプの動作が全て分かります。
5.2.1 プレハブ
パイプは「Pipe」というプレハブとしてAssetsに追加されているのでダブルクリックで内容を見ることができます。スクリプト上で利用する場合は上下に「Pipe」を配置しなければならないので少し手間です。従ってスクリプト上では「Pipe」は利用しておらず、予め「Pipe」を2つ利用して上下に配置したプレハブ「Pipe Set」を利用しているようです。また、パイプには「Rigidbody」コンポーネントが付けられていますが、デフォルトでは非表示に設定されており範囲が見えないので、範囲を可視化したい場合は「Grid and gizmo options」より表示設定を変更することができます。
5.2.2 PipesScript
①「Pipe Set」プレハブは下記のコードにより画面サイズに合わせて予め複数個作成されます。
②ランダムで高さを変更しています。
③スクロールが許可されると時間の経過とともにパイプの座標が更新されていきます。
④パイプの作成数を決定する箇所のログを表示してみると下記のようになっていました。
⑤ログの出し方
5.3 プレイヤー制御
プレイヤーはAssetsに「Character」というプレハブで追加されています。プレイヤーもパイプ同様に1つのスクリプトで動作しているため理解しやすいです。「CharacterScript」は「AsyncScript」として作成されているスクリプトですので、ゲーム起動時に「CountPassedPipes」、「DetectGameOver」というタスクを実行しておき、「Execute」という一度だけ実行される関数内でゲーム状態を判断してタップ判定や座標の計算を行っているようです。
5.3.1 CharacterScript
前述の「CountPassedPipes」、「DetectGameOver」というタスクですが、実際には別のスレッド実行されているということではなく、「Script.AddTask」でUnityで言うコルーチンが動作を開始しているとのこと。そして、内部のawaitによって「パイプを通り抜けた」、「地面、パイプに衝突した」といったことを検出するまで待機しています。また、プレイヤーの衝突判定は常に有効となっているため「CountPassedPipes」内の条件をゲーム起動時やゲームリセット時に満たしてしまうという不具合があります(ゲームリセット時にスコアがいきなり1になっていたりもします)。
①パイプを通り抜けたことを検出する時には「CustomFilter1」を利用しています。実は「Pipe Set」には「PassedTrigger」というRigidbodyを追加しただけのエンティティが付けられており、このRigidbodyのCollision Groupに「CustomFilter1」が設定されています。
②地面、パイプに衝突したことを検出する時には「DefaultFilter」を利用しています。先ほど「Grid and gizmo options」よりコライダーの形状を可視化したはずですが、地面のコライダーには「Infinite Plane」が設定されているため地面の衝突判定の形状は見ることができません(https://doc.xenko.com/latest/manual/physics/colliders.html)。
5.4 背景制御
JumpyJetの背景は主に「BackgroundSection(背景1枚分の制御)」スクリプトによって制御されています。背景画像をシーン上に描画させているのは「JumpyJetRenderer」の役割になりますが、シーン上を探しても見つけ出すことができません。通常プロジェクトを作成すると自動的に「Assets/GraphicsCompositor」が追加されますが、このスクリプトの「Entry Points」ノード内にて「JumpyJetRenderer」を指定することになっています。
これにより、スクリプトから「JumpyJetRenderer」を参照することができるようになりました。シーン上に配置されている「BackgroundScript」を開くと最初に「JumpyJetRenderer」を参照しており、その後はイベントの受信により背景スクロールの開始または停止が実行されます。
「JumpyJetRenderer」を完全に理解するためにはXenkoでのレンダリングパイプラインについて把握しておく必要があるので、ここでは背景をどのように動作させているかについて簡単に触れることとします。
①「ParallaxBackgrounds」プロパティにスプライトシートが設定されているため、「InitializeCore」関数内でスクロールさせる4枚の背景画像を生成します。
②背景のスクロール処理は「DrawCore」関数内で行われており、パイプのスクロール処理同様に時間の経過とともに座標が更新されます。
6.まとめ
今回は、Xenkoを利用して作られたサンプルプロジェクト「JumpyJet」について見てみました。スクリプトがC#で書かれているためUnityと比較してしまいますが、サンプル内でも継承をうまく使ったテクニックが多く、まだまだ情報が少ないため初心者が学ぶには少し難易度が高いと感じました。エディタをXenko内で実行できる点や、シーン上で複数のアセット、スクリプトタブを複数開いても強制的にまとめられず、2行目に表示されていた点は便利だと感じられました。
3Dに関して触れませんでしたが、Xenkoはグラフィック方面に力を入れているようですので試してみてはいかがでしょうか?今後は、「レンダリングパイプラインエディター」や「ビジュアルスクリプトシステム」も追加されるようですので、ますます使い勝手がよくなっていく可能性があります。
モバイルへのデプロイについてもサポート自体はされていますが、サンプルプロジェクトでビルドエラーが出てしまったりとまだ問題がありそうでした(要調査)。