i-school - スコアによる順位の変動管理
 NPC(ノン・プレイヤー・キャラクター、いわゆるコンピュータ側)とプレイヤーのスコアの値を基準に、スコアが高いほど順位を高くする順位変動処理の実装ロジックを考えます。

以下の内容で順番に実装を進めていきます。

・干支情報を作成し、List を利用してポイントの降順に干支情報を並べる
・ヒエラルキーの EtoInfo ゲームオブジェクトの順番も List の降順と同じ順番に並び替える



 新しい学習内容は、以下の通りです。

 ・List による管理
 ・List を利用して重複しない値を抽出して取得する方法
 ・OrderByDescending メソッド(Linq)
 ・SetSibilingIndex メソッド



干支情報を作成し、List を利用してポイントの降順に干支情報を並べる

1.設計


 便宜上スコアですが、他のゲームでも応用できるように、移行は point で統一します。

 ゲームにはまだ point という情報がありません。つまり、プレイヤーの point も、対戦相手となる NPC の point もありません。
管理したい情報がある場合には、それに紐づいたクラス、つまりスクリプトを作成して、そのクラスに必要となる情報を変数として管理させることを考えてください。

 スクリプトはいずれかのゲームオブジェクトにアタッチすることで有効になり、利用できるようになりますので、
必然的に、このクラスをアタッチするゲームオブジェクトの存在も必要になります。
 
 以上のことから、新しくスクリプトをアタッチするゲームオブジェクトを作成し、
そのゲームオブジェクトがプレイヤーや NPC の point を管理する、という役割を与えることになります。

 そのように考えていくことで、クラスやゲームオブジェクトの名称も、考えている役割に合ったものを設計出来るようになります。



 プレイヤーも含めて、NPC 側も、自分の point を管理するための情報用クラスを1つ用意し、これを管理用クラスで一元管理させます。
NPC 側として種類に応じた enum などを用意しておくといいでしょう。

 ゲームの開始時に、上記の情報用クラスをインスタンスし、NPC 側にはランダムに設定された point の値を配布します。
プレイヤーの pointは 0 からスタートします。

 このとき、生成したクラスを List で管理します。この List が順位の元になります。

 ゲーム内において point の加算処理が行われるたびに、すべての point の値を確認し、降順で並び替えます
こうすることで、プレイヤーの point に応じた順位が確定できます



 上記の流れを当てはめて、処理を設計しましょう。

 今回はわかりやすいように、NPC 側を対戦相手の情報を干支とします。そのため、まずは干支の種類を扱う enum を作成し、
その後、その enum を利用した EtoInfo (情報用)クラスを1つ作成します。この EtoInfo クラスでは、干支を1種類とその干支がもつ point を管理します。
UI 上で管理するので、アイコンを利用することも前提に Image も干支の画像と差し替えられるようにしておきます。

 最後に EtoInfo クラスを干支の数だけインスタンスし、ランダムに設定した point を配布、
その point の値を元に順位を降順で並び替える処理を行う、EtoInfoManager (管理用)クラスを作成します。
 

2.EtoType スクリプトを作成する


 今回はプレイヤーの対戦相手の想定として干支の種類を EtoType 型の enum として列挙子に登録しておきます。
enum のみを記述したスクリプトでは、using による宣言や、MonoBehaviour クラスの継承は不要です。

 そのためこのスクリプトは、ゲームオブジェクトにアタッチされないものの、ゲーム全体で利用可能な情報になります。


EtoType.cs

 <= クリックすると開きます



3.EtoInfo クラスを作成する


 1つ1つの干支の情報を扱うために、EtoInfo クラスを作成します。
これはゲームオブジェクトにアタッチして利用します。

 情報としては、干支の種類(EtoType)、point の値、干支の画像の情報、プレイヤーのクラスかNPC 用に利用しているか、を変数を宣言して管理します。
これはプレイヤーも NPC も共通で利用します。

 このクラスは始めは何も情報を持っていません。そのため、どの干支でもない状態ですが、
逆に考えると、どの干支にもなれる状態です。プログラムではこういった設計のものが多くあります。

 今回は、外部のクラス(今回は EtoInfoManager)より、干支の情報を引数で受け取り、それを設定することで
干支としての振る舞いを与えられるように設計しています。
例えば、丑の情報が引数でとどき、丑の画像を設定することで、このクラスは丑の干支の情報を管理するクラスになります。

 このような設計にしておくことで、干支の情報のクラスを12個別々に用意するのではなく、
1つのクラスに異なる情報を設定することで、12種類になるようにします。

 振る舞いを変える、という部分がプログラムでは非常に便利であるとともに、難しい部分でもありますので、
実際にどのように処理が動いて、ゲーム画面ではどのようになるのかを学習してください。
Unity ではクラスをオブジェクトとして実際のゲーム画面やエディターで確認できることを活用して理解を深めましょう。


EtoInfo.cs

 <= クリックすると開きます



4.EtoInfoManager クラスを作成する


 EtoInfo クラスを生成し、List において管理し、順番を変更したりする管理用のクラスです。
Canvas 内にゲームオブジェクトを作成し、そこにアタッチして利用します。

 干支の情報に関連する変数、干支の情報に関連する処理をメソッド単位でまとめてあります。

 どのように活用されているか、処理を記述しながら読み解いていってください。

 処理の流れは次のようになっています。

<事前準備>
 1.NPC 用に利用する point の値を points 配列にインスペクターより登録しておく

 2.干支情報のプレファブを etoInfoPrefab にアサインして登録しておく



<ゲーム実行時>
 1.Start メソッドにて SetRandomPoints メソッドが実行される。points 配列に登録した point がランダムに並び替えられた List が 作成される

 2.Start メソッドにて CreateEtoInfos メソッドが実行される。干支情報(EtoInfo)を持つゲームオブジェクトが13個生成される
   EtoInfo ゲームオブジェクトを生成した際に、干支の情報を設定するメソッドを実行し、干支としての振る舞いと point を付与される
   生成された EtoInfo ゲームオブジェクトの持つ EtoInfo クラスを etoInfoList に登録する。これで13種類の干支の管理が行える List が作成されて操作できる
   このとき etoInfoList は、生成された干支の順番通りに並んでいる(子 → 猫)の順番 
   最後に生成された EtoInfo をプレイヤーの情報として、myEtoInfo 変数に代入する。これで、プレイヤーの干支情報をいつでも操作できる

 3.Start メソッドにて SortByPoint メソッドが実行される。EtoInfo クラスの並び順が point を基準に降順で並び直される



<point 加算時(今回は同処理を Update メソッドにてデバッグ用に用意。本番もこの順番で処理を呼び出す)>
 1.AddPoint メソッドを実行し、myEtoInfo 変数の point が加算される。つまり、プレイヤーの point が加算される

 2.SortByPoint メソッドを実行し、EtoInfo クラスの並び順を point を基準に降順で並び直す。
   point 加算後に行うことで、最新の point の値で etoInfoList を並び直している



 記述を行いながら、処理の動きを確認してください。


EtoInfoManager .cs

 <= クリックすると開きます



5.処理を読み解く


 コメントはついていますが、処理の内容が理解できているかを確認しておきましょう。

 List の扱い方、一時的に List を作成して、値を順番に1つずつ値を抽出していく方法
OrderByDescending メソッドの活用方法など、しっかりと調べて、自分で活用できるようにしてきましょう。


 参考までに、OrderByDescending メソッドを読み解いてみましょう。

  // ポイントの降順で順番の並び替え
  etoInfoList = etoInfoList.OrderByDescending(x => x.point).ToList();

 OrderByDescending メソッドは Linq (リンク)と呼ばれる、C# の持つ機能の扱える、メソッドの1つです。
using System.Linq と宣言することで利用可能になります。Linq にはたくさんの便利なメソッドがあります。

 OrderByDescending メソッドを利用すると、対象となっている List に、引数で指定した情報を基準値に使って降順に並び替えてくれる処理を実行します。
今回の場合、List のすべての情報(EtoInfo)に対して、point 変数を基準に、EtoInfo クラスを1つずつ確認し、降順に並び替えています。つまり、戻り値を利用している処理になります。
それを最後に ToList メソッドを実行することによって、再度 List の形で処理を左辺へと戻しています。こちらも戻り値の処理です。
 

6.各ゲームオブジェクトを作成する


 Canvas 内で右クリックをしてメニューを開き、 Create Empty を選択します。
新しいゲームオブジェクトが作成されますので、名前を EtoInfoManager に変更します。


ヒエラルキー画像



 EtoInfoManager ゲームオブジェクトに、作成した EtoInfoManager スクリプトをドラッグアンドドロップしてアタッチします。
インスペクターを確認して、アタッチされているか確認します。




 EtoInfoManager ゲームオブジェクトの上で右クリックをしてメニューを開き、UI => Image を選択します。
Image コンポーネントがアタッチされているゲームオブジェクトが作成されますので、名前を EtoInfo に変更します。


ヒエラルキー画像



 EtoInfo ゲームオブジェクトに、作成した EtoInfo スクリプトをドラッグアンドドロップしてアタッチします。
imgEto 変数に、EtoInfo ゲームオブジェクトの Image コンポーネントをアサインしてください。


インスペクター画像


 設定が完了したら、プレファブにします。プレファブにしたら、ヒエラルキーからは削除してください。


7.EtoInfoManager ゲームオブジェクトの設定を行う


 EtoInfoManager ゲームオブジェクトを選択し、インスペクターから情報を設定します。

 EtoInfoPrefab 変数に、プレファブにした EtoInfo ゲームオブジェクトをドラッグアンドドロップしてアサインします。

 Points 配列変数の Size を変更し、13 にします。Element 0 〜 12 が追加されますので、適当に数字を設定します。
この値の1つがランダムに抽選されて、干支情報(EtoInfo クラス)の point 変数に登録されます。
ただし、そのまま利用すると常に同じ干支が同じ point になってしまうため、この Points 配列を利用した List を作成し、
その List を利用してランダムな値を干支に付与する設計とします。

 なお、Points 配列変数の Size の値は 13 以上であればいくつでも構いません。その分だけ、抽選範囲が増えます。


インスペクター画像


 EtoInfoList 変数、RandomPointList 変数、 MyEtoInfo 変数の3つは設定せずにおきます。
ここはゲームが実行されると自動的に値が入ります


8.ゲームを実行して動作を確認する


 ゲームを実行すると干支情報が生成されて、EtoInfoManager ゲームオブジェクトの子オブジェクトに 13個の EtoInfo ゲームオブジェクトが追加されています。

 EtoInfoManager ゲームオブジェクトのインスペクターも確認します。
 EtoInfoList 変数に代入されているか確認します。ヒエラルキーにある EtoInfo ゲームオブジェクトと並び順を確認してください。
ヒエラルキーは生成された順番に並んでいますが、EtoInfoList は EtoInfo クラスの point 変数の降順に並んでいます
 
 また MyEtoInfo 変数に、プレイヤーの干支情報が代入されていることも確認します。
プレイヤーの干支情報の場合、EtoInfo クラスの isMyEto 変数が true (スイッチオン)の状態になっていますので、
これと照合することで確認が行えます。

 RandomPointList 変数は EtoInfoManager クラスの points 配列の要素がランダムに代入されていれば成功です。
この順番で、ヒエラルキーにある干支情報は作られています。


インスペクター画像(Points 配列の要素と、RandomPointList の要素を比べてみる)



 デバッグ用に、Z ボタンを押すとプレイヤーの point だけ 100 ずつ加算されるようになっています。
加算していくと、point の値に応じて、etoInfoList の並び順も変わります。こちらも確認しておいてください。

 
 以上で、point の値を基準として、降順の List を作成する手順は終了です。
つぎは、この List の情報をヒエラルキーにある EtoInfo ゲームオブジェクトの並び順にも適用するようにします。
List の順番が降順になったらといって、ヒエラルキーにある EtoInfo ゲームオブジェクトが自動的に整列することはありません

 1つずつ処理を設計して、順番に実装をしていきます。


ヒエラルキーの EtoInfo ゲームオブジェクトの順番も List の降順と同じ順番に並び替える

1.設計


 List 内にある干支情報は、point 変数の値に合わせて降順で並び変える処理が実装されました。
これは EtoInfo クラスという情報は並び直されていますが、ヒエラルキーにある EtoInfo ゲームオブジェクトには反映されていません
つまり、ゲーム画面上も並び直しは反映されていない状態です。
そのため、point 変数に変動があってもヒエラルキーの EtoInfo ゲームオブジェクトはずっと同じ並び順になっています。

 この手順では、干支情報の List を利用し、この List の順番になるように、ヒエラルキーの EtoInfo ゲームオブジェクトを並び替える処理を実装します。

 並び替える順番については、point 変数の更新があるたびに List が更新されるため、この情報を利用することで同じ順番に並び替えるようにします。

 ヒエラルキーのゲームオブジェクトを並び替える際には、Transform コンポーネントの扱える SetSiblingIndex メソッドを利用します。
このメソッドは引数に指定した順番の位置に、ゲームオブジェクトを並び替えるメソッドです。

 そのため、List に管理されている干支情報の順番の番号をそのまま、このメソッドに利用することで並び替えが可能です。
干支情報クラスは、自分の Transform 情報を transform 変数から参照することが出来ますので、こちらを利用して、1つずつ順番を入れ替えていくようにします。


2.EtoInfoManager スクリプトを修正する


 EtoInfo ゲームオブジェクトを並び替えるメソッドを新しく1つ作成します。
このメソッドを、List の順番が降順に並び替えられたあとで呼び出すことで、List と同じ並び順で EtoInfo ゲームオブジェクトも並ぶように制御します。
 

EtoInfoManager .cs

 <= クリックすると開きます



3.ゲームを実行して動作を確認する


 処理を実装したら、読み返して、しっかりと内容を復習してください。

 ゲームを実行して、EtoInfo ゲームオブジェクトが生成されたら、ゲームを一時停止してください。

 ヒエラルキーの EtoInfo ゲームオブジェクトの順番を確認しましょう。
先ほどまでは、EtoInfoList の順番とヒエラルキーの EtoInfo ゲームオブジェクトの順番は不一致でしたが、
実装した処理によって、EtoInfoList の順番と合致していることが分かります。


<実装動画>
動画ファイルへのリンク


 また、デバッグ用に用意してある Z キーを押して、プレイヤーのポイントを加算してみてください。
この場合も、EtoInfoList の並び順が更新されるたびに、ヒエラルキーの EtoInfo ゲームオブジェクトも同じ並び順に変更になっています。


<実装動画>
動画ファイルへのリンク


 以上で完成です。