見出し画像

【Unity】3Dローグライクゲームの作り方〜Step14-10〜

前回の記事はこちら
前回はコインの預け出しを実装しました。

拠点セーブ-プレイヤー位置の保存

それでは拠点セーブできるようにしましょう。
まず最初にSaveDataManagerクラスを以下のように変更して下さい。

// 新しいパラメーターを追加
public bool isDungeon = true;

// メソッドを変更
private void Save()
{
   saveData.playerData = MakePlayerData();
   if (isDungeon)
   {
       saveData.enemyDatas = MakeEnemyDatas();
       saveData.itemDatas = MakeItemDatas();
       saveData.mapData = MakeMapData();
   }
   PlayerPrefs.SetString(saveKey, JsonUtility.ToJson(saveData));
}

private void Load()
{
   if (PlayerPrefs.HasKey(saveKey))
   {
       var data = PlayerPrefs.GetString(saveKey);
       JsonUtility.FromJsonOverwrite(data, saveData);
       if (isDungeon)
       {
           LoadMapData(saveData);
           LoadItemData(saveData);
           LoadEnemyDatas(saveData);
       }
       LoadPlayerData(saveData);
   }
}

private ActorSaveData MakeActorData(Transform actor)
{
   ActorSaveData actorSaveData = new ActorSaveData();
   actorSaveData.grid = new Pos2D();
   if (isDungeon)
   {
       ActorMovement move = actor.GetComponent<ActorMovement>();
       actorSaveData.grid.x = move.grid.x;
       actorSaveData.grid.z = move.grid.z;
       actorSaveData.direction = move.direction;
   }
   else
   {
       actorSaveData.grid.x = (int)actor.position.x;
       actorSaveData.grid.z = (int)actor.position.z;
       actorSaveData.direction =
           DirUtil.ReverseDirection(DirUtil.RotationToDir(transform.rotation));
   }
   ActorParamsController param = actor.GetComponent<ActorParamsController>();
   actorSaveData.parameter = param.GetParameter();
   actorSaveData.conditions = param.GetConditions();
   actorSaveData.itemInventory = MakeInventoryData(actor.GetComponent<ItemInventory>());
   if (param.parameter.id == EActor.PLAYER)
       actorSaveData.stoneInventory = MakeInventoryData(actor.GetComponent<StoneInventory>());
   return actorSaveData;
}

private void LoadActorData(ActorSaveData data, Transform actor)
{
   if (isDungeon)
   {
       ActorMovement move = actor.GetComponent<ActorMovement>();
       move.SetPosition(data.grid.x, data.grid.z);
       move.SetDirection(data.direction);
   }
   else
   {
       actor.position = new Vector3(data.grid.x, 0, data.grid.z);
       actor.rotation = DirUtil.DirToRotation(data.direction);
   }
   ActorParamsController param = actor.GetComponent<ActorParamsController>();
   param.SetParameter(data.parameter);
   param.SetConditions(data.conditions);
   LoadInventoryData(data.itemInventory, actor.GetComponent<ItemInventory>());
   if (data.parameter.id == EActor.PLAYER)
       LoadInventoryData(data.stoneInventory, actor.GetComponent<StoneInventory>());
}

SaveDataManagerをどこかのシーンから持ってきましょう。
そしてIs Dungeonフラグをfalseにし、Playerの項目にだけPlayerを設定しておきます。
実行してみます。SキーでセーブしてLキーでロードすると元の位置に戻ったかと思います。
ロードの際、向きが反転したかと思いますが、仕様です。反転させたくなければReverseDirectionメソッドを書かないようにすればいいです。

拠点セーブ-施設のインベントリの保存

次に施設のインベントリを保存できるようにしましょう。
SaveDataクラスを変更して下さい。

[System.Serializable]
public class SaveData
{
   public ActorSaveData playerData;
   public InventorySaveData shopData;
   public InventorySaveData wareHouseData;
   public ActorSaveData[] enemyDatas;
   public ItemSaveData[] itemDatas;
   public MapSaveData mapData;
}

次にSaveDataManagerクラスを変更します。

// パラメーターを追加
public Inventory shopInventory;
public Inventory wareHouseInventory;
private const string saveFilePath = saveDir + "/save.bin";

// メソッドを追加
/**
* セーブデータがあるか
*/
public bool ExistsSaveData() => File.Exists(Application.dataPath + saveFilePath);

// メソッドを変更
void Start()
{
   saveData = new SaveData();
   if (!isDungeon) ContinueStart();
}

void Update()
{
   #if UNITY_EDITOR
       if (isDungeon)
       {
           if (KeyConfig.GetKeyDown(KeyConfig.EType.EDITORSAVE))
           {
               Save();
               Message.Add("セーブしました!");
           }
           if (KeyConfig.GetKeyDown(KeyConfig.EType.EDITORLOAD))
           {
               Load();
               Message.Add("ロードしました!");
           }
       }
   #endif
}

public bool ContinueStart()
{
   if (PlayerPrefs.HasKey(continueFlag))
   {
       int flag = PlayerPrefs.GetInt(continueFlag);
       if (flag < 0)
       {
           Load(true);
           PlayerPrefs.SetInt(continueFlag, 0);
           return true;
       }
       else if (flag > 0)
       {
           Load(false);
           PlayerPrefs.SetInt(continueFlag, 0);
           return true;
       }
   }
   return false;
}

private void Save()
{
   saveData.playerData = MakePlayerData();
   saveData.enemyDatas = MakeEnemyDatas();
   saveData.itemDatas = MakeItemDatas();
   saveData.mapData = MakeMapData();
   PlayerPrefs.SetString(saveKey, JsonUtility.ToJson(saveData));
}

private void Load()
{
   if (PlayerPrefs.HasKey(saveKey))
   {
       var data = PlayerPrefs.GetString(saveKey);
       JsonUtility.FromJsonOverwrite(data, saveData);
       LoadMapData(saveData);
       LoadItemData(saveData);
       LoadEnemyDatas(saveData);
       LoadPlayerData(saveData);
   }
}

public void Save(bool isFlash)
{
   saveData.playerData = MakePlayerData();
   saveData.wareHouseData = MakeInventoryData(wareHouseInventory);
   if (!Directory.Exists(Application.dataPath + saveDir))
   {
       Directory.CreateDirectory(Application.dataPath + saveDir);
   }
   if (isFlash)
   {
       saveData.enemyDatas = MakeEnemyDatas();
       saveData.itemDatas = MakeItemDatas();
       saveData.mapData = MakeMapData();
       BinaryFormatter bf = new BinaryFormatter();
       FileStream file = File.Create(Application.dataPath + flashSaveFilePath);
       try
       {
           bf.Serialize(file, saveData);
       }
       finally
       {
           if (file != null) file.Close();
       }
   }
   else
   {
       saveData.shopData = MakeInventoryData(shopInventory);
       BinaryFormatter bf = new BinaryFormatter();
       FileStream file = File.Create(Application.dataPath + saveFilePath);
       try
       {
           bf.Serialize(file, saveData);
       }
       finally
       {
           if (file != null) file.Close();
       }
   }
}

public void Load(bool isFlash)
{
   if (isFlash)
   {
       string path = Application.dataPath + flashSaveFilePath;
       if (File.Exists(path))
       {
           BinaryFormatter bf = new BinaryFormatter();
           FileStream file = File.Open(path, FileMode.Open);
           try
           {
               saveData = (SaveData)bf.Deserialize(file);
               LoadMapData(saveData);
               LoadItemData(saveData);
               LoadEnemyDatas(saveData);
               LoadPlayerData(saveData);
               LoadInventoryData(saveData.wareHouseData, wareHouseInventory);
           }
           finally
           {
               if (file != null)
               {
                   file.Close();
                   File.Delete(path);
               }
                       
           }
       }
       else
       {
           Debug.Log("no load file");
       }
   }
   else
   {
       string path = Application.dataPath + saveFilePath;
       if (File.Exists(path))
       {
           BinaryFormatter bf = new BinaryFormatter();
           FileStream file = File.Open(path, FileMode.Open);
           try
           {
               saveData = (SaveData)bf.Deserialize(file);
               LoadPlayerData(saveData);
               LoadInventoryData(saveData.shopData, shopInventory);
               LoadInventoryData(saveData.wareHouseData, wareHouseInventory);
           }
           finally
           {
               if (file != null) file.Close();
           }
       }
       else
       {
           Debug.Log("no load file");
       }
   }
}

調節が大変だったのでSキー、Lキーはダンジョン内でしか使用できないことにしました。
また、とりあえずセーブできるのは1箇所のみにしました。

宿屋を選ぶとセーブ/スタート画面でロード

用意ができたところで、宿屋を選んだらセーブするようにしましょう。
InnActionクラスを変更します。

// パラメーターを追加
public SaveDataManager saveDataManager;

// メソッドを変更
private void Act()
{
   string method = subMenu.GetSelectItemMethod();
   switch (method)
   {
       case "Save":
           if (fade.Fade(true))
           {
               saveDataManager.Save(false);
               action = EAct.ActEnd;
           }
           break;
       case "WareHouse":
           wareHouseAction.Proc(true);
           action = EAct.TurnEnd;
           break;
   }
}

これでセーブはできたはずです。あとはデータをロードする必要がありますね。
StartButtonクラスを変更して下さい。

// メソッドを変更
private void Update()
{
   if (type > 0)
   {
       if (fade.Fade(true))
       {
           switch (type)
           {
               case 1:
                   type = 0;
                   SceneManager.LoadScene("TownScene");
                   break;
               case 2:
                   type = 0;
                   SceneManager.LoadScene("MainScene");
                   break;
               case 3:
                   type = 0;
                   Exit();
                   break;
           }
       }
   }
}

public void GameContinue()
{
   if (type == 0)
   {
       if (saveDataManager.ExistsFlashData())
       {
           type = 2;
           saveDataManager.SetContinueFlag(-1);
       }
       else if (saveDataManager.ExistsSaveData())
       {
           type = 1;
           saveDataManager.SetContinueFlag(1);
       }
       else type = 0;
   }
}​

装備品は入っていないはずですので以下を変更する必要があります。
ItemInventoryクラスを変更して下さい。

public override void SetAllItem(Item[] itemList, int maxNum)
{
   List<Item> items = new List<Item>();
   ActorParamsController.Equipment equipment = new ActorParamsController.Equipment();
   int itemCnt = itemList.Length < maxNum ? itemList.Length : maxNum;
   for (int i = 0; i < itemCnt; i++)
   {
       items.Add(itemList[i].Get());
       if (items[i].isEquip)
       {
           if (items[i].type == EItemType.Weapon)
           {
               if (equipment.weapon == null) equipment.weapon = items[i];
               else items[i].isEquip = false;
           }
           if (items[i].type == EItemType.Armor)
           {
               if (equipment.armor == null) equipment.armor = items[i];
               else items[i].isEquip = false;
           }
           if (items[i].type == EItemType.Accessory)
           {
               if (equipment.accessory == null) equipment.accessory = items[i];
               else items[i].isEquip = false;
           }
       }
   }
   itemNumMax = maxNum;
   this.items = items;
   // 以下2行を変更
   ActorParamsController param = GetComponent<ActorParamsController>();
   if (param != null) param.equipment = equipment;
}

実行してみます。宿泊した後一度終了し、StartSceneからContinueボタンを選択します。するとロードされたはずです。

スクリーンショット 2020-11-17 22.05.26

倉庫にブーツを入れてコインを550枚預けた状態でセーブ・ロードした

という訳で今回はここまでです。
次回はアイテム合成の外形から作成していく予定です。


この記事が気に入ったらサポートをしてみませんか?