💾

Unityで便利な外部ファイル式セーブ機能を作る

2022/12/16に公開

※この記事はwindows環境を想定して書かれています。mac環境でも大きく変わることはないとは思いますが、ご了承ください。

目次

・外部ファイル式セーブ機能(仮称)とは?
・とりあえず現場を見てみよう
・実際に作ってみよう

外部ファイル式セーブ機能(仮称)とは?

Unityにはもともと変数をひとつずつ記録する感じの機能があった気がしますが、それとは違い外部ファイル式セーブデータとは私が勝手に思いついて勝手に作ったセーブ方式のことです。下図のようなファイルを媒介としており、柔軟性に優れます。

見ていただいて分かる通り、ただのテキストファイルです。
使い道としては、RPG系ゲームのHPだとか所持アイテムだとかのセーブだったり、音ゲーの譜面の記録として使ったり。ソフト間やデバイス間でのデータの受け渡しとしても使えます。
そしてこのセーブデータはビルド後でも使えるため、セーブデータを書き換えることで好きな状態でテストプレイなんかができます。

とりあえず現場を見てみよう

私が実際に使っている様子を簡単にご紹介します。
使うセーブデータ↓
1000/0100/0100/0010/0100/0030/0051/1000=02/04/00/00/00/00/00/00/00/0193/00/00/00/00/00/00/00/00/0001/01/00/00/00/00/00/00/00/00=20=00/00/00/00/00/01/00/01/00/01/02/02/02/02/02=
 こちらはRPGゲーム用に作ったセーブデータです。このセーブデータを、下記のコードで読み取り

 test = File.ReadAllText ("save/test.txt");
test2 = test.Substring(0, 4);
stateID[0] = int.Parse (test2);

test2 = test.Substring(5, 4);
stateID[1] = int.Parse (test2);

test2 = test.Substring(10, 4);
stateID[2] = int.Parse (test2);

test2 = test.Substring(15, 4);
stateID[3] = int.Parse (test2);

int A = 0;
int B = 0;
while (A <= 29){B = A * 3 + 40;
test2 = test.Substring(B, 2);
ItemID[A] = int.Parse (test2);
A += 1;
}

test2 = test.Substring(130, 2);
sID = int.Parse (test2);

簡単に要約すると
体力 / 攻撃力 / 防御力 / ........ / アイテム1の数 / アイテム2の数 / ....
のような感じで読み取り、対応した変数に代入しているものです。なんとも野蛮なコードですね。

上記のものは完全に数値の位置を固定した方法ですが、特定の文字を基準にしたりすれば、もっと自由なセーブ方式にすることができます。
1Z42/2Z02/3B2/4B4/5G3/5.75Z02/6B3/6.5B2/7.5B4/8.5B4/9.5Z02/9.5G2/
受け取り側はこちら

while (test3 != "B" && test3 != "R" && test3 != "G" && test3 != "Z"){
S++;
test3 = test.Substring(S + L, 1);
}
test2 = test.Substring(L, S);

if (count % 20 != 0){}
else if (count != 0){result += "} else if (count < 20 + " + count + "){";}
else {result += "if (count < 20){";}

if (test3 == "B"){
result += "if (NM.time >= " + test2 + " + start - preset && count == " + count + "){count++; NM.G" + test.Substring(S + L + 1, 1) + ".note = 1;}";
}
if (test3 == "G"){
result += "if (NM.time >= " + test2 + " + start - preset && count == " + count + "){count++; NM.G" + test.Substring(S + L + 1, 1) + ".note = 2;}";
}
if (test3 == "Z"){
result += "if (NM.time >= " + test2 + " + start - preset && count == " + count + "){count++; NM.G1.note = " + test.Substring(S + L + 1, 2) + ";}";
L += 1;
}

count++;
L += 3 + S;
S = 0;

このコードではセーブデータ内の"Z"や"B"などを探し、それに対応した処理をそれぞれしてくれます。順番を変えても動いてくれるので、曲ごとに譜面が変わる音ゲーで重宝しています。

実際に作ってみよう

はい!実際に作りましょう!
こんなん文字で説明しても長ったらしくて理解なんてしてられないですからね。実際に作っちゃって、それを使いたい形に改造していくのがおすすめです。
※軽量化や視認性など細かいことを何にも考えていないコードです。ご了承ください。また、コードの細かい解説もしません...

0. 今回作るもの

・AとBとCをそれぞれ押すと、テキストボックスに「AAAABBBCCAAABBBBB」と表示されていく
・AとBとCが押されるたびに、セーブデータに記録されてく
・再度起動するとセーブデータが読み込まれ、「AAAABBBCCAAABBBBB」が表示される

1. てきとうにテキストボックスを用意する

結果をわかりやすくするため、テキストボックスを用意しておきましょう。

2. テキストボックスへ入力できるようにする(ABC)

test.csというスクリプトを作りました。
めんどくさいので、今回はこのスクリプト一つで完結させます。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//UIいじるのに必要↓
using UnityEngine.UI;

public class test : MonoBehaviour
{
public Text tx;

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {

if (Input.GetKeyDown(KeyCode.A)){tx.text += "A";}
if (Input.GetKeyDown(KeyCode.B)){tx.text += "B";}
if (Input.GetKeyDown(KeyCode.C)){tx.text += "C";}

        
    }
}

このスクリプトをメインカメラか何かに付け、txに先ほどのテキストボックスを代入してください。

開始してみて、実際に入力できればOKです。

3. 外部ファイルにセーブする

まずはセーブするための場所を用意しましょう。
プロジェクトファイル内にsaveというフォルダを作成し、
※ビルド後の場合は、起動用exeと同じフォルダ内に配置します。

そのsaveフォルダ内に、save1というテキストファイルを作成しました。

そしてtest.csの方に、入力する度にセーブするように書き足します。
(上の方にusing System.IO;が追加されているので注意!外部ファイル関係で必要です。)

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■↓ここから新しい記述 ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
using System.IO;
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■↑ここまで新しい記述 ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■

public class test : MonoBehaviour
{
public Text tx;

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {

if (Input.GetKeyDown(KeyCode.A)){tx.text += "A";}
if (Input.GetKeyDown(KeyCode.B)){tx.text += "B";}
if (Input.GetKeyDown(KeyCode.C)){tx.text += "C";}

// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■↓ここから新しい記述 ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
if (Input.GetKeyDown(KeyCode.A) || Input.GetKeyDown(KeyCode.B) || Input.GetKeyDown(KeyCode.C)){

string tex = tx.text;
string scan = tex.Substring(0, 1);
string save = "";
int S = 1;
int L = 1;

while (tex.Length > L){
if (scan != tex.Substring(L, 1)){
save += S + scan + "/";
S = 0;
scan = tex.Substring(L, 1);
}
S++;
L++;
}

save += S + scan + "/";

	StreamWriter sw = new StreamWriter("save/save1.txt",false);
	sw.WriteLine(save);
	sw.Flush();
	sw.Close();
}
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■↑ここまで新しい記述 ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
        
    }
}

これでセーブまでの流れが完成しました。試しに、AAABBBCCCABCとなるように入力してみてください。

そしてその後に、先ほど用意したsave1のテキストファイルを見てみると、

あら不思議!何かしらがセーブされています!
Aを3つ、Bを3つ...というセーブ方式にしてみました。

5. 起動時にセーブデータを読み込む

最後は大事な、読み取りです。記録できても再現できなきゃ意味ないですからね。
今回は、前回の入力状態をそのまま再現するように作っていきます。
test.csにセーブデータを読み取り、解析、再現するように書き足します。読み込み自体はそれだけです。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System.IO;

public class test : MonoBehaviour
{
public Text tx;

    // Start is called before the first frame update
    void Start()
    {

// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■↓ここから新しい記述 ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■
tx.text = "";

string tex =  File.ReadAllText ("save/save1.txt");
string scan;
int scanL;
int S = 0;
int L = 0;

while (tex.Length > L + 2){
scan = " ";
while (scan != "A" && scan != "B" && scan != "C"){
S++;
scan = tex.Substring(S + L, 1);
}
scanL = int.Parse(tex.Substring(L, S));
for (int i = 0; i < scanL; i++){
tx.text += scan;
}
L += S + 2;
S = 0;
}
// ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■↑ここまで新しい記述 ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■


        
    }

    // Update is called once per frame
    void Update()
    {

if (Input.GetKeyDown(KeyCode.A)){tx.text += "A";}
if (Input.GetKeyDown(KeyCode.B)){tx.text += "B";}
if (Input.GetKeyDown(KeyCode.C)){tx.text += "C";}


if (Input.GetKeyDown(KeyCode.A) || Input.GetKeyDown(KeyCode.B) || Input.GetKeyDown(KeyCode.C)){

string tex = tx.text;
string scan = tex.Substring(0, 1);
string save = "";
int S = 1;
int L = 1;

while (tex.Length > L){
if (scan != tex.Substring(L, 1)){
save += S + scan + "/";
S = 0;
scan = tex.Substring(L, 1);
}
S++;
L++;
}

save += S + scan + "/";

	StreamWriter sw = new StreamWriter("save/save1.txt",false);
	sw.WriteLine(save);
	sw.Flush();
	sw.Close();
}
        
    }
}

これで再度起動してみると、

なんと勝手にAAABBBCCCABCと入力されています!
やりましたね、セーブデータの読み込みに成功しました!
さて、せっかく完成したのでいろいろ遊んでみましょう。
セーブファイルに、20C/1A/ と記述しておきましょう。

起動してみると、

Cが20個、ちゃんと表示されましたね。
こんな風に、手入力でセーブデータをいじることができます。

おわり

以上が外部ファイル式セーブデータについてとなります。お好きなように改造して、自分の環境に合ったセーブ形式を作っちゃいましょう!おしまい!

Discussion