Unityに関連する記事です

 引き続き、ゲーム内で取得した必要な情報を、情報ごとにセーブ・ロードするための機能を実装します。
この手順では、List の情報をセーブ・ロードするための機能を実装します。


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

発展18 ーセーブ・ロード機能の実装◆
26.クリアしたステージの情報と契約したキャラの情報をセーブ・ロードする機能を実装する



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

 ・String.Split メソッドと StringSplitOptions 列挙型
 ・Linqの機能の実装例◆ Aggregate メソッド、ToList メソッド〜
 ・Listの使い方 ーConvertAll メソッドー



26.クリアしたステージの情報と契約したキャラの情報をセーブ・ロードする機能を実装する

1.設計


 前回の手順では int 型のクリアポイントの情報を PlayerPrefs クラスの SetInt メソッドと GetInt メソッドを 活用し、セーブ・ロード機能を実装しました。
この手順では、クリアポイントの他にもセーブ・ロードを行いたい情報を考えて、その変数をセーブ・ロードする機能を実装する設計を考えていきます。



 現在ユーザーの情報としてセーブしておきたい情報としては、クリアしたステージの情報と、契約しているキャラの情報があげられます。
どちらも int 型の情報ですが、List を活用して管理をおこなっています。

 PlayerPrefs クラスには int 型をセーブ・ロードするメソッドはありますが、List 型をそのままセーブ・ロードする機能はありません
つまり、List<int> 型そのままの状態ではセーブ・ロードができないため、少し手を加えてあげる必要があります。

 このようなときにはいくつかの方法が考えられます。(他にもあります)

<型が異なる際のセーブの方法>
 1.List の要素(中身)を1つずつ取り出して、int 型として1つずつ、セーブ・ロードを行う
 2.保存したいデータの型の作りをセーブ・ロードが行える型に変更してから、セーブ・ロードを行う

 今回は【2】の方法を採用します。



 参考までにですが、【1】の方法を解説しておきます。
for 文や foreach 文などを活用して、List の最大値を目標値に繰り返しの処理を作り、処理の中で、List の各要素に順番にアクセスして、それを int 型でセーブを行います。
ロードする場合も同様で、int 型の情報をロードして、それを List に順番に追加することで復元します。これは、List で管理している型がセーブ・ロードできる型である場合にのみ実装出来ます。

 ただし、この実装の場合、セーブ・ロード用の Key が各値ごとに必要になります。例えば、List の要素数が 5 個ある場合、5 つのセーブ・ロード用の Key が必要になります。
このような場合は、1つの Key 用の文字列を作成し、文字の最後に通し番号を採番することで各値のセーブ・ロード用の Key を自動的に作成できます。


<for 文を活用して List の情報をセーブ・ロードする場合の実装例>

    private const string ENGAGE_CHARA_KEY = "engageCharaNos_";  // セーブ・ロード用の Key(このままだと1つしかセーブできないので、最後に番号を追加する)

  // セーブ
    public void SaveEngageCharaListToInt() {

        // 契約したキャラの List から要素を 1つずつ取り出す
        for (int i = 0; i < engageCharaNosList.Count; i++) {
 
      // 1つずつセーブするために Key に採番し、セーブする値を設定
            PlayerPrefs.SetInt(ENGAGE_CHARA_KEY + engageCharaNosList[i].ToString(), engageCharaNosList[i]);

            // engageCharaNos_0 のような形でセーブされますので、ロードする場合も、engageCharaNos_0 のように、最後に番号を付ける必要があります
        }
        
        // SetInt メソッドの情報をすべてセーブ
        PlayerPrefs.Save();
    }

  // ロード
    public void LoadEngageCharaListToInt() {

        // 登録されているキャラのデータ数を目標値に繰り返す
        for (int i = 0; i < DataBaseManager.instane.charaDataSO.charaDataList.Count; i++) {
 
            // セーブされている Key があるか確認
            if(PlayerPrefs.HasKey(ENGAGE_CHARA_KEY + i)) {

          // ロードして List に追加
                engageCharaNosList.Add(PlayerPrefs.GetInt(ENGAGE_CHARA_KEY + i));

            }
        }
    }



 【2.保存したいデータの型の作りをセーブ・ロードが行える型に変更してから、セーブ・ロードを行う】方法についての設計の考え方です。

 型の作りをセーブ・ロードが行える型に変更する、という部分ですが、PlayerPrefs クラスでは3つの型でのセーブ・ロードが可能です。
List の情報は int 型ですが、この情報を、値(要素)ごとにカンマで区切られた1行の文字列として新しく変数を作成するようにします。

<List<int> をカンマ区切りの string 型にする>
  List<int> engageCharaNosList = new List<int>();

  engageCharaNosList[0] = 0;
  engageCharaNosList[1] = 1;
  engageCharaNosList[2] = 2;
  engageCharaNosList[3] = 3;

  ↓

  // List<int> を カンマ区切りの 1行の string 型にする
  string saveStr = "0, 1, 2, 3,";

 このように、情報を書き換えてあげることで、PlayerPrefs クラスのセーブ・ロードができる string 型になりますので、
セーブを行う際に string 型にして保存し、ロードを行う際にはこの string 型でロードして、その情報を元の型の情報に戻すことで、
PlayerPrefs クラスを利用してセーブとロードが行えるようにします。



 処理の流れをまとめます。

<セーブするとき>
 ・セーブしたい情報(クラス・型)が PlayerPrefs クラスに対応している型ではないため、そのままでは List<int> 型の変数の値をセーブ・ロードできない。
        ↓
 ・List<int> 型の情報を1つずつ取り出して、値と値とをカンマで区切った1行の文字列として作成し、セーブしたい情報を string 型の情報に変換する。
        ↓
 ・この string 型の情報を PlayerPrefs クラスの SetString メソッドと Save メソッドを利用してセーブする。

<ロードするとき>
 ・string 型でセーブしてある情報を PlayerPrefs クラスの GetString メソッドを利用してロードをする。
        ↓
 ・ロードした情報(クラス・型)が string 型のため、そのままではセーブする前の情報(今回は List<int> 型)として活用できない。
        ↓
 ・ロードした情報をカンマの位置で区切って、List<int> 型の値として1つ順番に取り出す。その際に、string 型を int 型に変換して List に追加することで情報を復元する。
        ↓
 ・セーブする際の情報(クラス)に復元されるので、この情報をまたゲームで利用する


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


 List<int> 型をセーブ・ロードを行える string 型にしてから処理を行う機能の実装を行います。
C# System の機能を利用しますので、using に System の宣言を追加しています。


GameData.cs

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


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


<String.Split メソッドと StringSplitOptions 列挙型>


 Split メソッドは String クラスが持つ機能の1つです。利用するためには、using System の宣言が必要になります。
Split メソッドには引数のオーバーロードが多くありますが、今回は、Split(String[], StringSplitOptions) の書式の引数を利用しています。
 

<参考サイト>
MicroSoft
String.Split メソッド
https://docs.microsoft.com/ja-jp/dotnet/api/system...


 今回の場合の Split メソッドは、第1引数に指定した文字列で、対象となる文字列内を区切って、1つの文字列を分割する場合に使用します。

  // カンマの位置で区切って、文字列の配列を作成。その際、最後にできる空白の文字列を削除
  string[] strArray = engageCharaListString.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);

 今回の場合であれば、engageCharaListString が分割の対象となる文字列です。
この文字列を先頭から調査していき、第1引数で指定している , (カンマ)の部分で区切り、分割します。分割後、それを配列として strArray 変数に代入しています。

 例えば、engageCharaListString 変数の値が {0, 1, 2} であるならば、これをカンマの位置で区切り、3つの文字列に分割しています。
それを配列として代入しています。



 StringSplitOptions 列挙型は、Split メソッドに指定できるオプション機能です。今回の引数の場合は、第2引数として設定できます。

 StringSplitOptions 列挙型には3種類の列挙子があり、この機能を活用すると、返された配列からの空の部分文字列を省略するかどうか、
または部分文字列から空白をトリミングするかどうかなどのオプションを指定することができます。

 今回は RemoveEmptyEntries の列挙子を指定していますので、文字列を分割していって空白の情報が作成された場合、それを省略(空白を削除)しています。
そのため、配列の要素には空白の情報が代入されないように制御しています。


<参考サイト>
MicroSoft
StringSplitOptions 列挙型
https://docs.microsoft.com/ja-jp/dotnet/api/system...


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


 GameData クラスの Awake メソッド内のデバッグの処理を活用して、デバッグを行います。
デバッグの方法は、前回のクリアポイントの場合と同じです。
Awake メソッド内にセーブ用のメソッドの呼び出し命令とロード用のメソッドの呼び出し命令がコメントアウトされていますので、
それを活用してデバッグを行ってください。


<考えよう!> セーブとロードするタイミングを決めて、処理の実装に挑戦する


 List のセーブとロードの処理のデバッグが無事に終了したら、次は、どのタイミングでセーブとロードを行うのかを考えて、
その部分にセーブとロードのメソッドを実行する処理を実装する必要があります。ロードについては、前回のクリアポイントと同じで
ゲーム開始時に1回だけ実行すればいいので、セーブする部分を考えてみてください。


<応用> ーGameData クラスのリファクタリングー


 GameData クラスの SaveEngageCharaList メソッドと LoadEngageCharaList メソッドの処理を戻り値と Linq のメソッドを活用してリファクタリングを行います。

 最初に SaveEngageCharaList メソッドからリファクタリングを行います。
戻り値を持つ ConvertListToString メソッドを新しく作成し、そのメソッド内で、
いままで SaveEngageCharaList メソッドにおいて処理していた内容を Linq に用意されているメソッドを利用して実装しています。


<SaveEngageCharaList メソッドのリファクタリング>

using System.Linq;    //  <=  追加します


  (中略)


    /// <summary>
    /// engageCharaNosList の値をセーブ
    /// </summary>
    public void SaveEngageCharaList() {


////* ここからコメントアウト *////


        // 新しく作成する文字列
        //string engageCharaListString = "";

        // 契約したキャラの List をカンマ区切りの1行の文字列にする
        //for (int i = 0; i < engageCharaNosList.Count; i++) {
        //    engageCharaListString += engageCharaNosList[i].ToString() + ",";
        //}

        // 文字列をセットしてセーブ
        //PlayerPrefs.SetString(ENGAGE_CHARA_KEY, engageCharaListString);


////* ここまで *////


        // 上記の処理を1行で記述します。SetString メソッドの第2引数内では ConvertListToString メソッドを実行し、string 型の戻り値を受け取り、それを第2引数として設定しています
        PlayerPrefs.SetString(ENGAGE_CHARA_KEY, ConvertListToString(engageCharaNosList));

        PlayerPrefs.Save();
    }

    /// <summary>
    /// int 型の List の値をカンマ区切りの1行の string 型に変換
    /// </summary>
    /// <param name="listData"></param>
    /// <returns></returns>
    public string ConvertListToString(List<int> listData) {

    // この処理は Linq の機能を利用して記述されています。
    // この処理は、GameData クラスの SaveEngageCharaData メソッド内の処理の、List<int> 型の情報を string 型(1行のカンマ区切りの文字列)にする処理と同じものです
        return listData.Select(x => x.ToString()).Aggregate((a, b) => a + "," + b);
    }



 続いて、LoadEngageCharaList メソッドのリファクタリングを行います。
現在は非常に長い処理になっていますが、こちらも、戻り値を持つ ConvertStringToList メソッドを新しく作成して、Linq で用意されているメソッドを活用することにより、
スマートな書式に書き換えることが可能です。


<LoadEngageCharaList メソッドのリファクタリング>
    /// <summary>
    /// engageCharaNosList の値をロード
    /// </summary>
    public void LoadEngageCharaList() {


////* ここからコメントアウト *////


        // 文字列としてロード
        //string engageCharaListString = PlayerPrefs.GetString(ENGAGE_CHARA_KEY, "");

        // ロードした文字列がある場合
        //if (!string.IsNullOrEmpty(engageCharaListString)) {

            // カンマの位置で区切って、文字列の配列を作成。その際、最後にできる空白の文字列を削除
        //    string[] strArray = engageCharaListString.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries);
            
        //    Debug.Log(strArray.Length);

            // 配列の数だけ契約したキャラの情報があるので
        //    for (int i = 0; i < strArray.Length; i++) {
        //        Debug.Log(strArray[i]);

                // 配列の文字列の値を int 型に変換して List に追加して、契約キャラの List を復元
        //        engageCharaNosList.Add(int.Parse(strArray[i]));
        //    }
        //}


////* ここまで *////


        //  上記の処理を1行で記述します。 ConvertStringToList メソッドの引数として GetString メソッドを実行し、List<int> 型の戻り値を受け取り、それを engageCharaNosList に設定しています
        engageCharaNosList = ConvertStringToList(PlayerPrefs.GetString(ENGAGE_CHARA_KEY, ""));
    }

    /// <summary>
    /// カンマ区切りになっている1行の string 型の値を int 型の List に変換
    /// </summary>
    /// <param name="str"></param>
    /// <returns></returns>
    public List<int> ConvertStringToList(string str) {

    // この処理は Linq の機能を利用して記述されています。
        // GameData クラスの LoadEngageCharaData メソッド内の処理の、string 型(1行のカンマ区切りの文字列)を List<int> 型にする処理と同じです
        return str.Split(',').ToList().ConvertAll(x => int.Parse(x));
    }


<Linqの機能の実装例◆ Aggregate メソッド、ToList メソッド>


 新しく実装している Linq のメソッドについて順番に説明を行います。
1.Aggregate メソッド

 アグリゲートと読みます。英単語としては、集計という意味を持ちます。

  listData.Select(x => x.ToString()).Aggregate((a, b) => a + "," + b);

 Select メソッドを利用して int 型の情報が string 型に型変換されていますので、その string 型の情報に対して、Aggregate メソッドが実行されます。
このメソッドは、作用する処理の型の情報を戻り値として持ちます。そのため今回であれば、string 型の戻り値を持つ処理になります。

 Aggregate メソッドの引数はラムダ式による記述を行う決まりになっています。
第1引数は a 変数を宣言している部分であり、ここには集計結果が代入されます。
第2引数は b 変数を宣言している部分であり、ここには Select メソッドの処理結果である string 型の List の情報が要素として代入されて、1つずつ順番に処理が行われます。
 
 処理の内容は、=> の右側の部分に設定します。今回は a + "," + b と設定していますので、List の最初の文字列に , (カンマ)を加えてから、List の次の文字列を足します。
これを、List の要素が無くなるまで繰り返し、最終的に string 型の情報が値として作成されます。

 例えば、listData 変数の値が {0, 2, 4} であったとした場合、最初に、Select メソッドが実行されて、この3つの値は int 型から string 型に型変換されます。
次に Aggregate メソッドが実行されて、string 型の List の要素が1つず準版に取り出されます。

 a + "," + b の部分に実数値を当てはめてみると

 a + "," + b
 
 0,2       (a = 0   b = 2)
 0,2,4     (a = 0,2   b = 4)

 という処理になります。よって、Aggregate メソッドの処理の結果、List の要素が1つの文字列の値になります。


<参考サイト>
MicroSoft
Enumerable.Aggregate メソッド
https://docs.microsoft.com/ja-jp/dotnet/api/system...
.NET Column 様
【C#とLINQ】Aggregateメソッドを使ってデータの集計をする方法
https://www.fenet.jp/dotnet/column/language/2048/
ゲーマーときどきエンジニア 様
【C#】【LINQ】Aggregateメソッドの使い方を解説します
https://www.tairax.com/entry/Csharp/LINQ-Aggregate


2.ToList メソッド

 配列や、Linq の Select メソッド、Where メソッドの結果を List のインスタンス(実体)として作成します。
Select メソッドなどの戻り値である IEnumerable型の値は抽出しただけで List の状態にはなっていませんので、抽出した情報を List として利用したい場合には
この ToList メソッドの処理を実行することにより List として値が作成されます。


<参考サイト>
MicroSoft
Enumerable.ToList<TSource>(IEnumerable<TSource>) メソッド
https://docs.microsoft.com/ja-jp/dotnet/api/system...
超初心者向けプログラミング入門 様
LINQの主要メソッド一覧
https://programming.pc-note.net/csharp/linq_method...
ヤスノートは今日も考える 様
[C# LINQ] 配列やSelect、Whereした結果をリストに変換する
https://yaspage.com/prog/csharp/cs-linq-tolist/


<Listの使い方 ーConvertAll メソッドー>


 List の持つメソッドの1つです。Array(配列)にも同名・同機能のメソッドがあります。

 ConvertAll メソッドを利用すると、引数に指定した要素をすべて別の型に変換(キャスト)処理を行うことが出来ます。

  str.Split(',').ToList().ConvertAll(x => int.Parse(x));

 x 変数の部分には List の要素が順番に代入されます。
今回の実装例では、string 型の List のすべての要素を ConvertAll メソッドを利用し、 int.Paruse メソッドによって int 型に型変換する処理を実行しています。
これにより、戻り値の型である List<int> 型の値が作成されています。

    public List<int> ConvertStringToList(string str) {
        return str.Split(',').ToList().ConvertAll(x => int.Parse(x));   // <= 処理が最後まで完了することで戻り値の型である List<int> 型の値になる
    }


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

ConvertAllメソッドを使って文字列型のListから数値型のListに変換する
https://smdn.jp/programming/dotnet-samplecodes/col...


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


 リファクタリングを行ったら、いままでと同じように処理が動くかどうかを確認しておいてください。


<考えよう!> クリアしたステージの情報をセーブ・ロードする機能の実装に挑戦する


 今回実装している List のセーブ・ロードの機能を応用して、もう1つの List<int> 変数であるクリアしたステージの情報についても
セーブ・ロードする機能の実装に挑戦してみてください。実装方法は、リファクタリング前後のどちらの処理でも構いませんが、
出来るならば、リファクタリング前の処理を実装してから、自分でリファクタリングして、処理をスマートに記述する方法を学習してみてください。

 最初からスマートな処理を書くことは難しいので、1つずつ順番に、実装とリファクタリングを繰り返して覚えていくといいでしょう。

 実装してデバッグが終了したら、契約キャラのセーブ・ロードと同じように、どのタイミングでセーブ・ロードを行うべきかを考えて、処理の実装にも挑戦してみてください。



 以上でこの手順は終了です。

 次は 発展19 ーセーブ・ロード機能の実装− です。

コメントをかく


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

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

Menu



プログラムの基礎学習

コード練習

技術/知識(実装例)

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

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

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

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

レースゲーム(抜粋)

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

3D脱出ゲーム(抜粋)

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

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

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

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

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

VideoPlayer イベント連動の実装例

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

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

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

private



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

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