【Unity】Unity側の値をCSVへ書き出す(Android実機も)

Unity側の値をCSVへ出力する方法についてです。

今回はUnity上での書き込みと、Android実機への書き込みも行います。

# 環境
- Windows10
- Unity Hub 2.4.5
- Unity 2020.3.25f1
- Android Studio 2020.3.1

# Unity上での書き込み
まずはじめにUnity上でCSVファイルの書き込みを行います。

## GameObjectの準備
まずはGameObjectの準備を行います。

以下の3つ作成いたします。

```
Cube > F
Cube > J
Empty > SaveCsv
```

適当に配置してOKです。

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399610/edea24d2-1ea1-1596-d1c8-e10b6c3747c3.png)

## Scriptの作成
以下のように作成します。

```c#:SampleSaveCsv
using System.IO;
using System.Text;
using UnityEngine;

// csvに保存するためのコード
// SaveCsvへアタッチ
public class SampleSaveCsv : MonoBehaviour
{
    // System.IO
    private StreamWriter sw;

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

        // 新しくcsvファイルを作成して、{}の中の要素分csvに追記をする
        sw = new StreamWriter(@"SaveData.csv", false, Encoding.GetEncoding("Shift_JIS"));

        // CSV1行目のカラムで、StreamWriter オブジェクトへ書き込む
        string[] s1 = { "F", "J", "time" };

        /**
         * s1の文字列配列のすべての要素を「,」で連結する
         * @see https://docs.microsoft.com/ja-jp/dotnet/api/system.string.join?view=net-6.0#System_String_Join_System_String_System_String___
         */
        string s2 = string.Join(",", s1);

        /**
         * s2文字列をcsvファイルへ書き込む
         * @see https://docs.microsoft.com/ja-jp/dotnet/api/system.io.streamwriter.writeline?view=net-6.0#System_IO_StreamWriter_WriteLine_System_String_
         */
        sw.WriteLine(s2);
    }

    // Update is called once per frame
    void Update()
    {
        // Enterキーが押されたらcsvへの書き込みを終了する
        if (Input.GetKeyDown(KeyCode.Return))
        {
            /**
             * @see https://docs.microsoft.com/ja-jp/dotnet/api/system.io.streamwriter.close?view=net-6.0#System_IO_StreamWriter_Close
             */
            sw.Close();
        }
    }

    public void SaveData(string txt1, string txt2, string txt3)
    {
        string[] s1 = { txt1, txt2, txt3 };
        string s2 = string.Join(",", s1);
        sw.WriteLine(s2);
    }
}
```

ちなみに「StreamWriterクラス」は、C#のクラスです。

公式ドキュメント

https://docs.microsoft.com/ja-jp/dotnet/api/system.io.streamwriter?view=net-6.0

`new StreamWriter` の第二引数にboolean値を渡していますが、
これは「true」だと同階層に同じファイル名があると既存のファイルへ新しい値だけを上書きします。

「false」だと値は全て新しくファイル自体を上書きします。

```c#:SampleF
using System.IO;
using UnityEngine;

// Fキーを検出するコード
// Fへアタッチ
public class SampleF : MonoBehaviour
{
    private float time;

    GameObject saveCsv;
    SampleSaveCsv sampleSaveCsv;

    // Start is called before the first frame update
    void Start()
    {
        /**
         * SaveCsvのスクリプトを参照する
         * @see https://docs.unity3d.com/ja/current/ScriptReference/GameObject.Find.html
         */
        saveCsv = GameObject.Find("SaveCsv");

        /**
         * コンポーネントを返す
         * @see https://docs.unity3d.com/ja/current/ScriptReference/GameObject.GetComponent.html
         */
        sampleSaveCsv = saveCsv.GetComponent<SampleSaveCsv>();
    }

    // Update is called once per frame
    void Update()
    {
        // Fキーが押されたら、csvに「F」と「いつ押されたか」という情報が書き加えられる
        time += Time.deltaTime;

        if (Input.GetKeyDown(KeyCode.F))
        {
            sampleSaveCsv.SaveData("F", " ", time.ToString());
        }
    }
}
```

```c#:SampleJ
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;

// Jキーを検出するコード
// Jへアタッチ
public class SampleJ : MonoBehaviour
{
    private float time;

    GameObject saveCsv;
    SampleSaveCsv sampleSaveCsv;


    // Start is called before the first frame update
    void Start()
    {
        /**
         * SaveCsvのスクリプトを参照する
         * @see https://docs.unity3d.com/ja/current/ScriptReference/GameObject.Find.html
         */
        saveCsv = GameObject.Find("SaveCsv");

        /**
         * コンポーネントを返す
         * @see https://docs.unity3d.com/ja/current/ScriptReference/GameObject.GetComponent.html
         */
        sampleSaveCsv = saveCsv.GetComponent<SampleSaveCsv>();
    }

    // Update is called once per frame
    void Update()
    {
        // Jキーが押されたら、csvに「J」と「いつ押されたか」という情報が書き加えられる
        time += Time.deltaTime;

        if (Input.GetKeyDown(KeyCode.J))
        {
            sampleSaveCsv.SaveData(" ", "J", time.ToString());
        }
    }
}
```

## それぞれへアタッチ
先程、作成した3つのScriptを以下のようにアタッチします。

```
SampleF.cs > F
SampleJ.cs > J
SampleSaveCsv.cs > SaveCsv
```

# PC上で動作確認
では、「Play」ボタンを押してキーボードの「FかJ」をタイピングしてみて下さい。

最後に「Enter」キーを押します。

プロジェクトフォルダ直下に「SaveData.csv」というファイルが作成され、以下のように1行目がタイトル、2行目からはどのボタンをいつ押したかが入力されています。

![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399610/c24beb5d-86df-8ecd-8f2e-48a3519070ef.png)

# Android実機への書き出し
続いてはAndroidへの書き出しを行います。

今のままのコードでは書き出しはされません。

Android用にコードを記述する必要があります。

## 端末ごとに処理を分ける方法
詳細は以前書いたこちらの記事をご参考下さい。

https://qiita.com/tak001/items/8054ae4ec9cde401f068

## SampleSaveCsvの修正
Start内を以下のように修正します。

```c#
void Start()
    {

#if UNITY_ANDROID
        // 新しくcsvファイルを作成して、{}の中の要素分csvに追記をする(Androidの処理)
        string FilePath = @"/SaveData.csv";
        sw = new StreamWriter(Application.persistentDataPath + FilePath, false, Encoding.GetEncoding("utf-8"));
#endif

#if UNITY_EDITOR
        // 新しくcsvファイルを作成して、{}の中の要素分csvに追記をする(Unity上での処理)
        sw = new StreamWriter(@"SaveData.csv", false, Encoding.GetEncoding("Shift_JIS"));
#endif

        // CSV1行目のカラムで、StreamWriter オブジェクトへ書き込む
        string[] s1 = { "F", "J", "time" };

        /**
         * s1の文字列配列のすべての要素を「,」で連結する
         * @see https://docs.microsoft.com/ja-jp/dotnet/api/system.string.join?view=net-6.0#System_String_Join_System_String_System_String___
         */
        string s2 = string.Join(",", s1);

        /**
         * s2文字列をcsvファイルへ書き込む
         * @see https://docs.microsoft.com/ja-jp/dotnet/api/system.io.streamwriter.writeline?view=net-6.0#System_IO_StreamWriter_WriteLine_System_String_
         */
        sw.WriteLine(s2);
    }
```

解説をすると、`Application.persistentDataPath` は機種依存のファイルパスで実機へ正しく書き込むために必要になります。

## GameObjectの追加
以下のように適当にButtonを配置します。

![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399610/917a8ec1-dd79-ce1d-97e6-4dfa2ae2a985.png)

## AddButtonのScriptを作成
今回はButtonをクリックしたタイミングでデータを追加したいと思います。

```c#:AddButton
using UnityEngine;

public class AddButton : MonoBehaviour
{
    private float time;

    GameObject saveCsv;
    SampleSaveCsv sampleSaveCsv;

    // Start is called before the first frame update
    void Start()
    {
        saveCsv = GameObject.Find("SaveCsv");
        sampleSaveCsv = saveCsv.GetComponent<SampleSaveCsv>();
    }


    public void onClick()
    {
        time += Time.deltaTime;
        // テキストデータも適当です
        sampleSaveCsv.SaveData("B", " ", time.ToString());
    }
}
```

## AddButtonへアタッチ
では、AddButtonへアタッチします。

1. AddButtonオブジェクトへAddButton.csをアタッチ
1. Inspectore > Button の「On Click()」へAddButton.csのイベントを設定(以下図参照)

![4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399610/f2a54d96-521c-05d5-fa6d-3ddae6d7bd46.png)

## SDカードへ書き込みをする場合は設定が必要
もしSDカードへ書き込みを行う場合、Unity側で少し設定が必要です。

Edit > Project setting > Player > Other Settings の下の方に
「Write Permisson」という項目があります。

こちらの項目を「External(SDCard)」へ変更します。

![5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399610/2c94fcf4-3f65-7fb2-c60e-3507ce8ca881.png)

# Android実機で動作確認
では、実機とPCを繋いで「Build And Run」をしてみましょう。

buildされたら、「Add Button」を何度かタップしてみます。

## データの確認
データの確認ですが、保存先が端末によって異なります。

以下は一例になります。

「内部ストレージ/メモリーカード」 > 「Android」 > 「data」

このフォルダまで来るとインストールされているパッケージが沢山あると思います。

自分で作ったパッケージネームを探します。

パッケージネームはUnity側の、
Edit > Project Settings > Player > Other Settings の
「Package Name」から確認できます。

![6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/399610/5443d153-8680-2062-3e52-b20688a47d6f.png)

次のフォルダで「cache」と「files」があると思いますので、
「files」を選択します。

すると、「SaveData.csv」が保存されています。

これでAndroid実機へのcsv書き込みもできました。

# まとめ
- ファイルの書き込みには **StreamWriter** を使う
- #if で端末の判定を行い実機によるpathを使い分ける
- Androidの保存先pathは **Application.persistentDataPath** を使う

以上ご拝読有難うございました。