Proceduralデモ
イントロダクション
このデモでは、手続き型で生成されたワールドとPhoton Cloudを使ってそのワールドに適用された修正の扱い方について説明します。PUN2パッケージには、キューブやブロックでできたワールドを作成するデモがあります。作成手順にはいくつかあって、異なるワールドを作成できます。
このドキュメンテーションページでは、デモと生成されたワールドが動作する方法と、全てのクライアント間で同期された修正の適用の仕方について説明します。Photon Cloudを使って手続き型で生成されたワールドの作成時に、一般的によくある間違いも紹介しています。
このデモの使い方
ルームに接続すると、MasterClientはコントロールパネルを使って決定的ワールドジェネレータを制御できます。コントロールパネルと、関連するオプションについては次の章で説明します。ワールドジェネレータを実行しているとき、クラスタが複数作成されます。このクラスタには、ワールドを形づくるブロックが複数含まれています。ワールドを数個のクラスタに分けると、のちに修正が適用されてワールドを同期する際に便利です。同期がどのように動作するかというのもこのドキュメントページのトピックに含まれています。ワールドへの修正の適用は、どのクライアントでもブロックを左クリックすると高さが減少され、右クリックすると増加します。
ブロックの高さの増減とは、何のことでしょうか。生成されたワールドのさまざまなレベルの高さを表すのに、Y軸上のブロックのスケールを使用します。この第一のメリットは、シーンに存在するGameObjectsがそこまで多くないことで、パフォーマンスに負担をかけません。Unity(その他のエンジンでもそうですが)は無限にオブジェクトを扱えるわけではありません。このデモの目的では、この実装で十分です。
もう1つの側面はどうにかして適用された修正を保存して、全クライアントで利用できるようにしなければなりません。Photon Cloudで使用できるカスタムサーバーサイドロジックがない(Enterprise Cloudを使用していない限り)ので、適用された修正を保存するためにカスタムルームプロパティを使用します。
最後のポイントとしては、実装したソリューションのほうが、複雑で「リリース可能な」ソリューションを使うよりも理解しやすいということです。ただ、このデモでは手続き的に生成したゲームの開発にPhoton Cloudを使用している場際の可能性を表しています。
ワールドを生成する
デモを再生していると、ゲームウィンドウの左上にあるコントロールパネルが目に入ります。このパネルは、ワールドジェネレータを制御するので、使えるのはMasterClientのみとなっています。このデモでは、ワールドジェネレータに影響を与える4つのオプションがあります。
このオプションの一つが、デモ内の数字に代表されるシード値です。シード値は最小1digit、最大10digitとする必要があり、結果的に0から9,999,999,999の間の値となります。もう1つのオプションではワールドの全体的なサイズを表します。このデモでは、16 x 16から128 x 128ブロックの次元を持つワールドの作成が可能です。このデモの目的により、生成されたワールドは無限ではありません。
オプションの3つ目は、クラスタのサイズを表しています。クラスタ1つに対し、64個までのブロックが含まれます。生成されるクラスタの数は、おもにここの値と、前述のワールドのサイズに依存します。ブロックの数が多いクラスタは、ワールドを作成するスピードが速くなります(今回の実装による)。
4つ目のオプションはワールドのタイプを表すもので、これは生成されたレベルの見た目に影響します。デモには異なる3つのオプションが含まれていて、生成プロセスの間、ブロックの高さの最大値に影響します。
MasterClientがコントロールパネルで確認ボタンを押すと、選択されたオプションがカスタムルームプロパティに保存され、同じルームにいる全クライアントが使用できるようになります。クライアントはこれらのアップデートを受信すると、ローカルでワールドジェネレータを(再)実行し、新しいワールドを作成します。これらのオプションが同期され、Randomの機能がもう必要ではなくなるので、各クライアントは、同じワールドを生成します。
ワールドを生成するにはノイズ機能であるSimplex Noiseアルゴリズムを活用します。インスタンス化された各ブロックのx-およびz-coordinatesをCalcPixel2D機能にパスします。2D機能の他に、このBenjamin Ward によるSimplex Noise実装は1Dと3Dの機能も用意しています。これらの機能にはそれぞれ、共通する大事な側面が2つあります。側面の1つめは、これらの機能はいつも-1.0fから1.0fの間隔で値を返すということです。この値は各ブロックの高さ(y-scale)の計算に使用します。高さはおもにこの値と既に出てきたワールドのタイプに依存します。2つめは、この機能の出力は、入力が同じである限りいつも同じであるということです。言い換えると、入力パラメータを換えると、異なる結果が出てくるということです。例えば、シード値がそうです。ワールドジェネレータで使用したシード値やその他のオプションを同期しなければならないのは、おもにこの理由によります。
修正を同期する
前述の通り、ワールドに適用された修正は、カスタムルームプロパティに保管されています。ここに保管されていることで大きなメリットがあります。それは、各クライアントは自動的に、全ての修正を含む最新のカスタムルームプロパティを受信することになるので、クライアントサイドの処理だけで済むということです。後から参加してきたクライアントについても、「自動的に」処理されるので、最新のワールドの状況をどう共有するかについて考える必要がありません。
修正したデータをカスタムルームプロパティに保管するために、各クラスタは一意の識別子とディクショナリを追加します。ディクショナリそのものに特定のブロックの一意の識別子と関連する高さ(y-scale)が含まれています。ここで大切な側面は、カスタムルームプロパティに保管されるのは修正されたブロックのみだということです。修正されていないブロックについては、前で説明したワールドジェネレータの設定で表現されていて、カスタムルームプロパティに保管される必要はありません。
このデモの目的にはこの方法で何の問題もありません。しかし、例えばスケールの大きいゲームを「無限の」ワールドで作成する場合には、Enterprise CloudかセルフホスティングのPhoton Serverの使用を検討してください。この2つのオプションでは、Pluginの使用や自身でのサーバーアプリケーションの実装によってカスタムサーバーサイドロジックを実行することが可能です。ワールドサイズの上限やゲーム参加時の読み込み時間など、回避できる制限もあるので、ゲームに不可欠な場合があります。
よくある間違い
この章では、手続き型で生成されたネットワーキングゲームの作成においてよくある間違いを紹介しています。
起こり得るまず第一の間違いは、全てを「ネットワークインスタンス化」しようとすることです。いくつかの壁で構成された迷路の作成を例にとってみましょう。PUNの観点からいうと、簡単なアプローチはこの壁のプレハブを作成し、作成したプレハブにPhotonViewコンポーネントをアタッチして、Unityエディタで作業している間にシーンの中に直接置くか、実行時にPhotonNetwork.InstantiateもしくはPhotonNetwork.InstantiateSceneObjectを使用してインスタンス化するかします。これはある程度の量のオブジェクトを処理できますが、お勧めできない理由の一つは、クライアントごとのViewIDに制限があるからです。これはシーンオブジェクトと同様にユーザーにも適用されます。この制限によって、生成された迷路もサイズや複雑さに制限される可能性があります。
もう1つのよくある間違いは、UnityかSystemネームスペースから各クライアントひとりひとりにRandomクラスを使用することです。Randomクラスの問題点は、同じシード値を使用しない限り、マシンによって違う結果が返されてしまうことです。結果としてクライアントごとに違うワールドを生成することになり、マルチプレイヤーゲームが成り立たなくなります。それでもなお同期されたシード値を使って、Randomクラスを使おうとすると…さらなるデメリットにぶつかります。視覚的に満足のいく結果にはならないでしょう。Noiseアルゴリズムが(生成に際しての設定によって)異なる高さレベル間で推移するハイトマップを作りますが、Randomクラスを使うと、その推移がうまくいかなくなります。うまくいかないどころか、つぎはぎだらけで見ていて残念な結果に終わるでしょう。
カスタムルームプロパティを使って保管できることがわかったので、生成したワールド全体も同じ方法で保管しようと思う方もいるかもしれません。ワールドがあまり大きくならないうちであれば、この方法でも対応可能ではあります。しかしここでの問題は、クライアントに伝達するデータが大量にあるためルームの参加に時間がかかり過ぎてしまうことです。この場合の解決策はサーバーサイドロジックを追加してサーバーにクライアントに送るべきデータを選別させることです。つまり、一度にワールド全体の全データを送るかわりに、サーバーはクライアントが現在必要としているワールドの一部分だけのデータを送り、必要に応じて後からアップデートを行います。
Back to top