iT邦幫忙

2022 iThome 鐵人賽

DAY 26
1

平常我們會將資料進行讀取與寫入到 .csv 檔,最近因為實驗室實驗的資料需要儲存,我希望能將我過程中的一些數據能同步到 Excel 儲存,方便我之後統計各項數據。所以這兩天我將會根據如何儲存資料到 .csv 的檔案,以及如何寫入資料到 .csv 的檔案。這邊會希望大家將你想要儲存的.csv檔案記得與我們的Scene 新增的環境同一個資料夾下。 那我們就開始今天寫入資料的實作。而因為主要我要做寫入資料為準,所以我這邊實做會比較複雜一些。

建置場景

  1. 我們先新增幾個 text 到環境中,顯示路徑檔、新增與刪除狀態、文件資料的 id。

Excel 檔案儲存變更

  1. 一般的 Excel file 是無法進行儲存的,必須要轉變成 ".csv 逗號間隔” 格式才能在 Unity 中使用。所以在 Excel 中另存新檔,選擇 CSV(逗號分隔)。

  2. 接著就會在 Unity 看到之前族儲存的 UserEndStudy 變成如下樣式在下面。

.csv 檔案路徑搜尋 (重要)

  1. 因為你今天要去使用檔案中的 Excel file 所以說務必要知道該檔案的位址。我們在該 UserEndStatus CSV檔案上按下右鍵。 點下 Show In Explorer 就可以看到該檔案的位址。

  2. 接著就複製該檔案資料的路徑。 如下

E:\NYCU\Lab\Project\Seated Teleport\Unity Project\Teleport\Assets\Teleport_Main\DataExportExcel\Data

  1. 但注意到這只是知道這個檔案目前確切在哪一個資料夾,所以要記得該 .csv 檔案的名稱。建議先複製起來。注意檔案格式 .csv 不要少加。
UserEndStudy.csv

  1. 接著就可以開始撰寫文本。

Script 文本撰寫

宣告變數與引入函數

  1. 新增文本去 ShowFileUI

  2. 引入以下 函數庫避免等一下要用到的 StreamWriter 會產生錯誤。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using System;
using System.IO;
  1. 接下來宣告一下之後會用到的 UI text 變數。
// UI text from the GUI
    public Text filePathText;
    public Text fileStatus;
    public Text fileId;
  1. 將剛剛複製的路徑檔設為變數,以及檔案的名稱(注意要加上 .csv 檔案格式) 這邊宣告為 const 目的就是該變數是 "不可變動使用的"。
  • 以下注意到 @ "filePath” : 就是可以直接將檔案位址輸入,系統會直接變成可用的格式。
  • 注意到 "/ExcelFileName.csv” : 撰寫的就是說明該 .csv 檔案的名稱與格式。
private const string FILEPATH = @"E:\NYCU\Lab\Project\Seated Teleport\Unity Project\Teleport\Assets\Teleport_Main\DataExportExcel\Data";                  // here change to your .csv file path
private const string EXCELNAME = "/UserEndStudy.csv";
  1. 接下來就是要記錄該 data 的 Id 。 以及紀錄該檔案讀寫狀態的 string
private int file_id = 0;
private string statusStr;
  1. 要去使用該類別做 “資料結構化”。目的就是透過 class + List 來實現資料管理。 受先宣告一個新的 class 。 當然你也可以新增文本寫在裡面,但以下的方法可以直接實現在Script 當中。
[System.Serializable]
public class DataManager
{
}
  • 接下來就是去定義該內容要儲存那些參數。
[System.Serializable]
public class DataManager
{
	public int id;
	public string name;
	public float time_need;
	public int fatigue;
	public string Now_time;
}
  1. 需要透過 List 來儲存每一筆新增的資料,每一次新增的資料都會包含上面 class 內部的每一個參數,所以說非常重要。以下宣告。
private List<DataManager> UsersDataList = new List<DataManager>();

這也就是每一次新增該List 內部的元素時,都要去將所有的資料進行儲存。

新增與刪除 List 資料

  1. 首先我們撰寫新增物件 (DataManager) 的物件到 List 中。注意到 List 內部 id, Now_time 都是系統本身會自己添加,我們不需要變動,所以說 void 傳入的數值僅三個 name, time_need, fatigue。
public void AddListElement(string _name, float _TpTime, int _TpNumber)
    {
        if(file_id >= 0)
        {
            file_id++;
            UsersDataList.Add(new DataManager(){id = file_id, name = _name, time_need = _TpTime, fatigue = -_TpNumber, Now_time = GetStudyTime()});
        }else{
            Debug.LogWarning("List id empty!");
        }
        
    }
  • 每次新增一個物件都會增加一次 file_id。 同時傳入的資料請注意到與宣告 DataManager 的物件相同,並且將所有的資料參數都要輸入。當然若沒有輸入就會是預設為 0。
UsersDataList.Add(new DataManager(){id = file_id, name = _name, time_need = _timeNeed, fatigue = _fatigueNum, Now_time = GetStudyTime()});
  • 這邊因為要取得今天的日期與相關的時間參數,所以使用。 GetStudyTime 而該回傳值的 function 在 下方。也可以自行變更該呈現的格式。
 Now_time = GetStudyTime()

string GetStudyTime() 
    {
        PlayerPrefs.SetString("date time", System.DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"));
        return PlayerPrefs.GetString("date time");
    }
  1. 刪除資料的方式要注意到每一次刪除都會讓 file_id 減 1。這裡要注意到 List 元素實際的位置與 id 的位置差 1。所以說這邊就會先扣掉 1 再刪除 List 資料。 比如最後的 id 為 6 但其實在 list 中元素的資料位置為 5。
public void DeleteListEelement()
    {
       if(file_id > 0)
       {
            file_id--;
            UsersDataList.RemoveAt(file_id);
       }else{
        Debug.LogWarning("List is empty!");
       }
        
    }
  1. 若想要確認目前List的資料數量與容量可以使用到以下方式
void showTheList() 
    {
        Debug.Log("Capacity: " + UsersDataList.Capacity);
        Debug.Log("Count: " + UsersDataList.Count);
    }

以列儲存相關資料

  1. 這邊我們會需要將每筆資料形成列的樣式進行儲存。

  2. 儲存資料需要傳入進兩個參數 fileName 檔案名稱、 dataList 你想要儲存的資料的 List ,這個 List 就包含所有你之前存進去的資料內容。

  3. List 內部就是一個 Obj,每個 Obj 都包含各個變數,如同以下呈現。

  4. 所以說要如何去使用該 List 資料內的參數呢? 可以透過以下方式進行。 這邊我是一次覆寫掉所有的資料在 Excel 中,也就是一次刷新所有的 Excel內的資料。

foreach(var obj in ListName)      // var is the DataManager, obj is DataManager object 
{
	// then store the obj.eachDataName 
}
  1. 接著我們要使用到 StreamWriter 的物件,也就是說我們要透該 StreamWriter 的物件加上該檔案的路徑去儲存該資料到指定的路徑區。所以說要傳入兩個東西。
  • string fileName : 就是你要儲存 excel 資料的檔名。
  • List<"DataManager"> dataList : 就是你要儲存的 List 名稱。
    完整的程式碼如下:
void WriteToCSV(string fileName, List<DataManager> dataList)
    {
        using(var dataFile = new StreamWriter(FILEPATH + fileName))                     // here you can change the fileName to get file
        {
            dataFile.WriteLine(returnDataRowName());
            foreach(var ele in dataList)
            {
                dataFile.WriteLine($"{ele.id}, {ele.name}, {ele.time_need}, {ele.fatigue}, {ele.Now_time}");
            }
            dataFile.Close();
        }

    }
  1. 程式碼說明:
  • 首先需要使用到 StreamWriter 並將該資料的路徑檔傳進去內部,注意到這邊
    FILEPATH + fileName 就是說 "你的路徑檔 + 你的 .csv 名稱”
using(var dataFile = new StreamWriter(FILEPATH + fileName))
{

}
  • 接著撰寫內部的資料。首先就是顯示第一個 row 你想要先設為什麼名稱。也就是第一個row 你想要什麼變數的名稱,也就是每個行最上面的資料名稱。這邊使用 returnDataRowName( )
dataFile.WriteLine(returnDataRowName());


string returnDataRowName() 
    {
        return "Id, Name, Need Time, Fatigue, Date Time";
    }

  • 接下來就是要去新增資料到 Excel 中,我們這邊就是透過 List 中 DataManager 的物件進行一列一列的輸入,每一個物件都會有自己的資料。並且進行儲存,將其儲存完成後就關閉檔案。
    這邊注意到傳入進去的資料透過逗號進行區隔,所以這邊輸入進去的時候要記得,以下表示:
dataFile.WriteLine($"{ele.id}, {ele.name}, {ele.time_need}, {ele.fatigue}, {ele.Now_time}");
 foreach(var ele in dataList)
 {
    dataFile.WriteLine($"{ele.id}, {ele.name}, {ele.time_need}, {ele.fatigue}, {ele.Now_time}");
}
dataFile.Close();
  • 完整的程式碼:
void WriteToCSV(string fileName, List<DataManager> dataList)
    {
        using(var dataFile = new StreamWriter(FILEPATH + fileName))                     // here you can change the fileName to get file
        {
            dataFile.WriteLine(returnDataRowName());
            foreach(var ele in dataList)
            {
               dataFile.WriteLine($"{ele.id}, {ele.name}, {ele.time_need}, {ele.fatigue}, {ele.Now_time}");
            }
            dataFile.Close();
        }

    }

撰寫控制程式

  1. 首先希望按下鍵盤 L 進行資料的新增,按下 k 資料刪除。所以下列就是控制的程式。
if(Input.GetKeyDown("l"))
        {
            statusStr = "Data into Excel List!";
            AddListElement("Kevin", UnityEngine.Random.Range(0f, 10f), -(UnityEngine.Random.Range(1, 100)));
            fileId.text = file_id.ToString();                                                                       // start to show 1
            WriteToCSV(EXCELNAME, UsersDataList);
        }else if(Input.GetKeyDown("k"))
        {
            statusStr = "Data remove Excel List!";
            DeleteListEelement();
            fileId.text = file_id.ToString(); 
            WriteToCSV(EXCELNAME, UsersDataList);
        }

實際Demo (Demo 時請關閉 Excel)

  1. 首先開始程式會出現該UI。 會顯示目前資料的位置。

  2. 之後按下 L 新增八筆資料。看到以下的 UI 顯示。 接下來關閉 Unity 。

  3. 接下來就可以看到 UserEndStudy 會出現我們的資料!!! 但注意到若程式重新開啟執行就會覆寫掉這些剛剛傳出的數值。

  4. 在執行的過程中是可以開啟 Excel 但若要做任何資料讀寫的動作是不允許的,所以要關閉再開來檢查比如,以下先新增13個資料。開啟 Excel 檢查。

關閉 Excel 後!!! 接著去刪除掉資料到剩下 8個。接著開啟 Excel 。 成功就會看到剩下 8個資料了XD

結論:

  1. 今天透過 Unity 來讀取該以逗號做為區隔的 .csv file。 以上方式讓我們可以將實驗中的資料儲存在我們指定的檔案。
  2. 理解如何撰寫一個可以將資料以列的形式儲存在 csv 檔案中。

上一篇
Day25: Save and Load System in Unity
下一篇
Day27: Read from Excel .csv file
系列文
Unity 基本功能實作與日常紀錄30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言