Unityに関連する記事です

 ここからは3回に分けて、お使いの時間のデータをゲーム内に反映し、放置ゲームとして動かすためのシステムを実装します。
まずは最初に、お使いの経過時間をセーブ・ロードできるように OfflineTimeManager クラスにてお使いごとの時間の経過を管理できるようにするための処理を実装します。


<実装動画(完成時)>
動画ファイルへのリンク


手順14 −お使いの時間の経過を管理する処理の実装−
24.OfflineTimeManager スクリプト、GameManager スクリプト、TapPointDetail スクリプトを修正し、お使いの時間データを OfflineTimeManager スクリプトにて管理できるようにする



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

 ・クラス内に別のクラスを作成する(入れ子クラス)
 ・List の使い方◆ Find メソッド、Exists メソッド、Add メソッドー
 ・new 演算子によるインスタンスの作成とオブジェクト初期化子



24.OfflineTimeManager スクリプト、GameManager スクリプト、TapPointDetail スクリプトを修正し、お使いの時間データを OfflineTimeManager スクリプトにて管理できるようにする

1.設計


 お使いの残り時間の情報は TapPointDetail スクリプトに用意している JobData クラスにて管理を行っています。
そのため、行き先用のゲームオブジェクトが複数ある場合、それらは独立して個々に存在している関係上、
お使いの残り時間の情報を把握して、管理することが困難な状態になっています。

 時間の管理については、シングルトンデザインの OfflineTimeManager クラスのスクリプトを作成してありますので、
こちらにて各行き先用のゲームオブジェクトが管理している JobTime クラスの中から、お使いの残り時間と、それに関連する情報を集約して管理できる設計にします。

 管理しておくことが必要な情報を精査し、必要な情報を持ったクラスを作成して、その情報を List にて管理を行います。
例えば、残り時間の情報は必要ですが、お使いの名前の情報は使う機会がありませんし、通し番号が分かれば検索可能になる情報のため、不要です。

 この時間データ用のクラスを OfflineTimeManager クラスが管理することが出来るようになってはじめて、
お使いの残り時間をセーブ・ロードする処理を実装出来るようになります。

 今回の実装では、お使いを開始すると、お使いを開始した時間や、お使いの残り時間を管理するクラスを作成して、
それを OfflineTimeManager クラスで管理できる状態までロジックを考えて組み込んでいきます。

 修正が必要なスクリプトは、OfflineTimeManager、GameManager、TapPointDetail の各スクリプトです。
このうち、GameManager スクリプトに関しては、前回の手順で TODO 機能で用意してある部分に処理を実装していくことを考えます。


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


 お使いの時間データを管理するための JobTimeData クラスを OfflineTimeManager スクリプト内に作成します。
このクラスでは、お使いの通し番号、お使いの残り時間、お使いを開始した時間を保持するための変数を用意しておきます。

 お使いを開始したタイミングでこの JobTimeData クラスをインスタンスして、選択している行き先の内容の情報を、各変数に代入して取得します。
そして、予め用意しておいた、JobTimeData クラス型の List に登録します。
ただし、同じお使いの番号は重複して登録されないように List に JobTimeData クラスを追加する前に確認を行います。

 このように1つの List に集約して JobTimeData クラスを管理しておくことで、現在どのお使いが実行されているのかを、OfflineTimeManager クラスがいつでも確認できる状態を作り出しています。
複数の同じクラス(型)の情報を扱う場合には、このように List などのコレクションを活用した設計にしておくことで利便性に富む内容になります。

 この一連の処理を実装するために、宣言、フィールドにはクラスの宣言、List の宣言を行い、新しいメソッドを3つ追加しています。


OfflineTimeManager.cs

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


 スクリプトを修正したらセーブします。



 OfflineTimeManager ゲームオブジェクトのインスペクターを確認してください。
新しく public 修飾子で追加した List 型の変数がインスペクターに表示されていると思います。


インスペクター画像



 こちらは Size 0 のままで問題ありません。
ゲームを実行し、お使いを開始した段階で、この List 用のクラスが生成されて、List に追加される処理が実行されます。

 今回実装している処理の内容になりますので、どのタイミングでどの処理が実行されているために、このような制御が行えているのか、流れを確認しておいてください。


3.<クラス内に別のクラスを作成する(入れ子クラス)>


 OfflineTimeManager クラスの宣言フィールドにおいて、別のクラスを作成しています。今回は JobTimeData クラスです。
このようにC#では、1つの独立したクラス(ファイル)としてではなく、あるクラスの中の宣言フィールドに別のクラスを作成しても使用することができます。
このような構造を入れ子(ネスト)クラスと言います。

public class OfflineTimeManager : MonoBehaviour
{
    // 宣言フィールド


    /// <summary>
    /// お使い用の時間データを管理するためのクラス
    /// </summary>
    [Serializable]
    public class JobTimeData {
        public int jobNo;              // お使いの通し番号
        public int elaspedJobTime;     // お使いの残り時間
        public string jobTimeString;   // DateTime クラスを文字列にするための変数

        /// <summary>
        /// DateTime を文字列型で保存しているので、DateTime 型に戻して取得
        /// </summary>
        /// <returns></returns>
        public DateTime GetDateTime() {
            return DateTime.ParseExact(jobTimeString, FORMAT, null);
        }
    }

 特定のクラスでのみ使用することが確定しているような、使用範囲の狭いクラスであれば、このように入れ子クラスにした方がスクリプト・ファイルが増えずに済みます。
 また設計上、ファイルにはしたくない(隠しておきたい)クラスを作成する場合にも用いられます。

 使用方法は他のクラスと同じです。参照する場合は、OfflineTimeManager.JobTimeData という書式で、入れ子クラスのあるクラスの後に、入れ子クラスを順番に記述します
[System.Serializable] 属性を付与しておくと、インスペクター上に表示させたり、別のオブジェクトに変換することが出来るようになります。


4.<List の使い方◆ Find メソッド、Exists メソッド、Add メソッドー>


 今回利用している List クラスの持つメソッドについて説明します。


1.List.Find メソッド

 List 内の要素を先頭から検索して、指定した条件に合致した最初の要素1つを取り出して戻り値として返してくれる処理です。
これは foreach 文で記述する内容と同じ処理を1行で処理できます。

<Find メソッド>
  // セーブ対象の JobTimeData を List から検索して取得
  JobTimeData jobTimeData = workingJobTimeDatasList.Find(x => x.jobNo == jobNo);

<foreach 文を利用した同じ処理>
  foreach (JobTimeData jobTimeData in workingJobTimeDatasList) {
      if(jobTimeData.jobNo == jobNo) {
          return jobTimeData;
      }
  }

 この2つの処理は同じ内容になります。
最初に合致した値が対象になりますので、まとめて複数取得したい場合や、合致する条件を満たす要素が複数ある場合には、この処理だけでは実装できません



 左辺には、検索結果が代入されるための変数を用意しておきます。(Listが管理している型と同じ型の変数を宣言します。)
ここでは workingJobTimeDatasList 変数の List に対して Find メソッドの処理を行うため、workingJobTimeDatasList の管理している JobTimeData 型の変数を宣言します。

 Find メソッドの処理はラムダ型で記述します。
x => x.JobNo 

 この部分は、workingJobTimeDatasList の要素(中身である JobTimeData クラス)を1つずつ順番に取り出して、その JobTimeData クラス内の管理する jobNo を使って照合を行う、という処理です。
workingJobTimeDatasList には実行中のお使いの数だけの要素(お使いの時間データの数)がありますから、その数分だけ、最初の要素から順番に照合を行ってくれます。
x の値には、その都度、1つだけ JobTimeData クラスが代入されるようになります。


x.jobNo == jobNo

 この部分は、1つずつ取り出されてくる JobTimeData クラスの情報と、メソッドで届いている比べる対象である jobNo の値が同じかどうか照合判定しています。
x.jobNo とは、現在取り出されている JobTimeData(これが x に当たります)クラスの持つ jobNo を見ています

 そして照合の結果、合致した値が見つかった場合には、その値を左辺へと戻してくれます。Find メソッドはその時点で未検索の workingJobTimeDatasList の要素があっても処理が終了します。



 メソッド・チェーンを利用することにより、次のように、戻り値を活用した処理を記述することも出来ます。

  // List から該当の JobTimeData を検索して取得し、elaspedJobTime の値を currentJobTime に更新
  workingJobTimeDatasList.Find(x => x.jobNo == jobNo).elaspedJobTime = currentJobTime;

 この処理ではまず最初に Find メソッドが実行されて、検索条件に該当した JobTimeData クラスが1つ取得されます。
その JobTimeData の持つ elaspedJobTime 変数に、currentJobTime 変数の値を代入するという処理になります。


参考サイト
MicroSoft
List<T>.Find(Predicate<T>) メソッド
https://docs.microsoft.com/ja-jp/dotnet/api/system...
Samurai Blog様
【C#入門】Listの要素を検索するFindの使い方(FindAll/FindIndex)
https://www.sejuku.net/blog/45252


2.List.Exists メソッド

 List<T> に、指定された述語によって定義された条件と一致する要素が含まれているかどうかを判断します。

  // お使いを List に追加する前に、すでにリストにあるか確認して重複登録を防ぐ
  if (!workingJobTimeDatasList.Exists(x => x.jobNo == jobTimeData.jobNo)) {

 Find メソッドとの違いは、Find メソッドの場合は、List 内に List で定義しているクラスの情報があるかどうかを判定し、そのクラスの情報を戻り値で取得しています。

 Exists メソッドは、戻り値が bool 型になっており、List 内に条件に該当する情報があるか、ないか、を判定するメソッドになっています。
上記のケースでは false の場合が条件を満たすことになりますので、条件式に該当する情報が List 内にない場合のみ、if 文内の処理を行う作りになっています。

参考サイト
MicroSoft
List<T>.Exists(Predicate<T>) メソッド
https://docs.microsoft.com/ja-jp/dotnet/api/system...


3.List.Add メソッド

 Listの末尾に引数で指定した要素(データ)を追加します。
引数が T 型となっていますが、これは List を宣言した際に使った型が自動的に入ります。それ以外の型の情報を指定すると追加できないためエラーになります。
今回は JobTimeData 型の List を宣言していますので、引数には JobTimeData 型のみ指定できます。

 Listで宣言している型と同じ型であれば Add メソッドで List に要素を追加することが出来ます

 // List にない場合のみ、新しく追加する
 workingJobTimeDatasList.Add(jobTimeData);

 追加された要素は、自動的に List の最後に順番に追加されていきます。
例えば、workingJobTimeDatasList がまだ1つも要素がなければ、workingJobTimeDatasList[0] として1つ目に要素が追加されます。
そのあとに Add メソッドが実行された場合には、workingJobTimeDatasList[1] として2つ目に要素が追加されます。

参考サイト
MicroSoft
List<T>.Add(T) メソッド
https://docs.microsoft.com/ja-jp/dotnet/api/system...
SamuraiBlog 様
【C#入門】Listの使い方総まとめ(ArrayList/Add/Remove/ソート/検索)
https://www.sejuku.net/blog/47378


5.<new 演算子によるインスタンスの作成とオブジェクト初期化子>


 クラス(型)のインスタンスは new 演算子を使用することで作成することが出来ます。
また、オブジェクト初期化子 { } を使用すると、オブジェクトの作成時にアクセスできるフィールド(にある変数)またはプロパティに、
コンストラクターを呼び出して代入ステートメントを使用しなくても、値を割り当てることができます。


<クラス。JobNo 変数などがフィールドの情報>
    [Serializable]
    public class JobTimeData {
        public int jobNo;              // お使いの通し番号
        public int elaspedJobTime;     // お使いの残り時間
        public string jobTimeString;   // DateTime クラスを文字列にするための変数
  }

<new 演算子によるインスタンス作成処理とオブジェクト初期化子 { } による初期化処理>
  // JobTimeData をインスタンスして初期化
  JobTimeData jobTimeData = new JobTimeData { jobNo = tapPointDetail.jobData.jobNo, elaspedJobTime = tapPointDetail.jobData.jobTime };

 この機能により、新しく作成された JobTimeData クラスの各変数には、{ }(オブジェクト初期化子)内で指定された値情報が、それぞれ代入処理されます。

参考サイト
MicroSoft
new 演算子 (C# リファレンス)
https://docs.microsoft.com/ja-jp/dotnet/csharp/lan...
MicroSoft
オブジェクト初期化子とコレクション初期化子 (C# プログラミング ガイド)
https://docs.microsoft.com/ja-jp/dotnet/csharp/pro...


6.GameManager スクリプトを修正する


 前回の手順で TODO を記述してあった場所のうち、JudgeSubmitJob メソッド内に処理を実装していきます。

 自分で実装をしていく際のコツは TODO 機能ももちろんですが、日本語でコメントとして、実装したい処理を順序立ててスクリプト内に書いておく、ということです。
この日本語のコメントの処理をプログラム化するにはどうすればいいのか、という観点で捉えてみてください。

 今回も処理を記述する前に、「どんな処理を」「どこに書けばいいのか」をイメージして、処理を追加していってください。
そのような状態で処理を書いていくのと、教材にあるから書いている、とでは学習内容に大きな差が生まれますので、是非、教材をご自分にとってよい状態で利用していってください。


GameManager.cs

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


 スクリプトを修正したらセーブします。


7.TapPointDetail スクリプトを修正する


 お使いの残り時間を反映させるために、PrapareteJobs メソッドと WorkingJobs メソッドに引数を追加し、この値を利用して、お使いの残り時間を設定できるようにしています。
今までは固定値を利用していたため、この処理がないと、ゲーム終了中の時間の経過をゲーム内に反映させることができないため、放置ゲームのシステムが完成しないためです。

 また、WorkingJobs メソッド内にも処理を追加し、OfflineTimeManager クラスにあるお使いの時間データの残り時間を、現在の残り時間と同期させるようにしています。
この処理がないと、OfflineTimeManager クラスのお使いの時間データの残り時間は初期値のまま変化しないため、
ゲームを終了して再開するたび、常に初期値からお使いの残り時間が開始されてしまいますので、この処理によってこの現象を防いでいます。


TapPointDetail.cs

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


 スクリプトを修正したらセーブします。


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


 処理の内容、処理の流れをしっかりと把握した上で、ゲームのデバッグを行って、動作の検証と確認を行います。

 お使いを開始したときに、OfflineTimeManager スクリプトの WorkingJobsTimeDatasList 変数に、お使いの情報(JobTimeData クラス)が作成されて、List に登録されること、
そしてその中の elaspedTime 変数の値が、TapPointDetail スクリプトのお使いの残り時間の値と同期していることを確認してください。

 この2点が問題なければ制御成功です。(下記の動画では JobTimeString の値も書き込まれていますが、このタイミングではまだ表示されなくて問題ありません。)


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


 以上でこの手順は終了です。
 
 次は 手順15 −お使いの時間データをセーブする処理の実装− です。

コメントをかく


「http://」を含む投稿は禁止されています。

利用規約をご確認のうえご記入下さい

Menu



プログラムの基礎学習

コード練習

技術/知識(実装例)

2Dおはじきゲーム(発展編)

2D強制横スクロールアクション(発展編)

3Dダイビングアクション(発展編)

2Dタップシューティング(拡張編)

レースゲーム(抜粋)

2D放置ゲーム(発展編)

3D脱出ゲーム(抜粋)

2Dリアルタイムストラテジー

2Dトップビューアドベンチャー(宴アセット使用)

3Dタップアクション(NavMeshAgent 使用)

2Dトップビューアクション(カエルの為に〜、ボコスカウォーズ風)

3Dトップビューアクション(白猫風)

VideoPlayer イベント連動の実装例

VideoPlayer リスト内からムービー再生の実装例(発展)

AR 画像付きオブジェクト生成の実装例

AR リスト内から生成の実装例(発展)

private



このサイト内の作品はユニティちゃんライセンス条項の元に提供されています。

管理人/副管理人のみ編集できます