Unity進(jìn)階技巧 - 從文件讀取游戲數(shù)據(jù)

CSV游戲數(shù)據(jù)表

前言

在游戲制作中,我們的許多數(shù)據(jù)都是需要從文件里面讀取,比如常用的裝備數(shù)據(jù),怪物數(shù)據(jù),關(guān)卡數(shù)據(jù)等等,所以如何從文件中讀取這些數(shù)據(jù)就變得尤為重要,因?yàn)閷⒂螒驍?shù)據(jù)放入文件中,會(huì)大大提高我們制作和調(diào)整游戲的效率,所以本例中我們來看看Unity中如何通過CSV文件來讀取游戲數(shù)據(jù)

你將學(xué)到什么?

  • 使用Numbers制作CSV數(shù)據(jù)文件
  • Unity基礎(chǔ)的文件讀取
  • 通過行數(shù)和列數(shù)獲得指定的數(shù)據(jù)

一、制作CSV文件

一般情況下,制作游戲數(shù)據(jù)會(huì)用到表格軟件,windows下常用Excel,而Mac下常用Numbers,而這兩種軟件都可以導(dǎo)出我們本例所需要的文件——CSV文件

首先我們打開Numbers,新建一個(gè)文件

新建一個(gè)Numbers文件

然后輸入我們需要的內(nèi)容,如下:

表格內(nèi)容

這就簡(jiǎn)單的制作了一個(gè)裝備數(shù)據(jù)表,每件裝備我們假設(shè)它有5個(gè)屬性:

  • id:裝備ID,具有唯一性
  • name:裝備的名字
  • level:裝備等級(jí)
  • attack:裝備增加的攻擊
  • def:裝備增加的防御

數(shù)據(jù)填寫完之后,我們就可以選擇文件->導(dǎo)出到->CSV…

導(dǎo)出CSV

然后在彈出的對(duì)話框,直接點(diǎn)擊下一步:

下一步

然后在接下來的對(duì)話框中,選擇文件名和保存路徑:

選擇文件名和保存路徑

導(dǎo)出后,我們找到myTest.csv文件,打開可以看到:

**myTest.csv**文件內(nèi)容

我們可以看到我們剛剛制作的表格數(shù)據(jù)都被以文本的方式保存下來了,并且使用了“,”號(hào)來進(jìn)行分割,而CSV的中文翻譯就是逗號(hào)分隔值。但是我們會(huì)發(fā)現(xiàn)一個(gè)問題,為什么后面會(huì)多了那么多逗號(hào)呢?那是因?yàn)槲覀儗?dǎo)出的時(shí)候沒有將空的單元格刪除掉

都是空單元格的錯(cuò)

所以我們導(dǎo)出CSV文件是,記得要?jiǎng)h除掉那些空的單元格,如下圖:

刪除空單元格后的表格

然后我們?cè)賹?dǎo)出一次,并打開導(dǎo)出的文件查看,就會(huì)發(fā)現(xiàn)那些多與的逗號(hào)沒有了

終于正常的CSV文件

二、讀取CSV文件

有了CSV文件后,下一步就是將它放入U(xiǎn)nity中,并讓Unity讀取里面的數(shù)據(jù),首先我們新建一個(gè)Unity工程,然后將我們剛剛創(chuàng)建的CSV文件放入U(xiǎn)nity的資源目錄下,本例中我們選擇放入在*Asset->Res文件夾下面(Res文件夾需要大家自己創(chuàng)建)

CSV文件放在Unity中的位置

然后我們新建一個(gè)名為CSV的腳本,打開進(jìn)行編輯,輸入一下代碼:

完整的代碼
  • 首先,如果我們要使用Unity的讀取文件的方法,需要在開頭引入System.IO的命名空間,這樣編輯器才會(huì)識(shí)別讀取文件的一些方法
  • 然后我們還需要使用List<T>類型的變量,所以還要引入System.Collections.Generic的命名空間
  • 然后這個(gè)CSV類是我們自己定義的一個(gè)靜態(tài)類,不需要繼承MonoBehaviour類,所以我們把繼承MonoBehaviour類的語句刪除掉
  • 接著我們定義了csvm_ArrayData兩個(gè)成員變量,第一個(gè)用來實(shí)現(xiàn)單例模式,第二個(gè)用來保存從文件讀取的內(nèi)容
  • 然后使用單例模式來生產(chǎn)這個(gè)類的實(shí)例,以后需要使用這個(gè)類的方法我們就通過GetInstance來獲取這個(gè)類的實(shí)例
  • 接著我們?cè)跇?gòu)造函數(shù)CSV()中,初始化m_ArrayData
  • 然后我們定義了loadFile方法,有兩個(gè)參數(shù),第一個(gè)是路徑名,第二個(gè)是文件名,通過路徑名+文件名我們就可以指定我需要讀取的文件了
  • 接著看看loadFile這個(gè)方法里面到底做了什么事情?首先每次讀取文件前,我們清空一下m_ArrayData,以免數(shù)據(jù)沖突
  • 然后我們定義了一個(gè)StreamReader類型的變量sr,用他來保存文件讀取后的最原始的數(shù)據(jù)
  • 然后我們使用try{} catch{}語句,來捕獲程序異常,這個(gè)邏輯和if語句有點(diǎn)想,如果我們文件讀取不成功,就會(huì)運(yùn)行catch里面的語句,在后他打印一句話來通知我文件沒有找到,在try{}里面使用OpenText方法來打開我們的myTest.csv文件
  • 接著我們定義了一個(gè)line,用來臨時(shí)保存sr里面的每一行數(shù)據(jù)
  • 然后使用一個(gè)while循環(huán),把sr里面的數(shù)據(jù)按照一行來切割,全部放入m_ArrayData
  • 最后使用close和Dispose函數(shù)將sr進(jìn)行關(guān)閉和銷毀

寫完代碼,保存一下,然后我們?cè)趧?chuàng)建中新建一個(gè)空的GameObject,命名為FileController,并且為掛載一個(gè)新的腳本文件FileController,用來控制文件的讀取。

FileController

然后打開FileController腳本,輸入下面的代碼:

完整的代碼
  • 首先我們?cè)赟tart函數(shù)中,通過CSV的實(shí)例調(diào)用loadFile方法來讀取我們的myTest里面的內(nèi)容,loadFile方法有兩個(gè)參數(shù),一個(gè)是路徑名,一個(gè)是文件名稱,關(guān)于路徑名的一些注意事項(xiàng),會(huì)在后面的擴(kuò)展閱讀里面介紹,這里只要理解我們通過路徑名+文件名就可以指定需要讀取的文件了
  • 然后我通過for循環(huán),將m_ArrayData里所有的內(nèi)容打印在程序后臺(tái)中

寫完代碼,保存一下,然后回到Unity編輯器,運(yùn)行游戲,可以在后臺(tái)打印中看到如下數(shù)據(jù):

后臺(tái)打印的數(shù)據(jù)

可以看到,現(xiàn)在我們已經(jīng)可以讀取到文件里面的內(nèi)容了,并且是可以分解每一行的內(nèi)容分別是什么,但是這樣的解析程度還遠(yuǎn)遠(yuǎn)不夠,不能為我們所用,假設(shè)我們游戲中需要生成一件裝備布衣,我們需要通過文件獲得布衣的每一個(gè)屬性,如布衣的等級(jí),增加的防御值等屬性,我們就需要將每一個(gè)數(shù)據(jù)單獨(dú)提取出來,所以我們需要可以指定獲得某一個(gè)單元格里面的數(shù)據(jù)才行,下面就來看看怎么實(shí)現(xiàn)

三、根據(jù)行數(shù)和列數(shù)來指定獲取數(shù)據(jù)

現(xiàn)在我們的m_ArrayData里面存放的是一行的數(shù)據(jù),也就是說其實(shí)m_ArrayData[0]就是取我們第一行的數(shù)據(jù),m_ArrayData[1]就是取我們第二行的數(shù)據(jù),那么接下來我們只需要再進(jìn)行一下解析,將每一行的數(shù)據(jù)按照列數(shù)來拆解,這樣我們就可以通過行數(shù)+列數(shù)來確定指定數(shù)據(jù)了,先直接上代碼:

Paste_Image.png
  • 由于這次我們需要將每一行的數(shù)據(jù)按照列數(shù)來拆解,那么m_ArrayData里面裝的元素就不再是string了,而是一個(gè)string數(shù)值,所以我們這邊需要修改m_ArrayData的類型為List<string[]>
  • 然后我們新增一個(gè)方法getString,它有兩個(gè)參數(shù),row是行數(shù),col是列數(shù),方法返回指定行數(shù)和列數(shù)的數(shù)據(jù),數(shù)據(jù)類型是string
  • 同時(shí)我們還增加一個(gè)方法getInt,它也有兩個(gè)參數(shù),row是行數(shù),col是列數(shù),方法返回指定行數(shù)和列數(shù)的數(shù)據(jù),數(shù)據(jù)類型是int,所以最后調(diào)用了int.Parse方法將string轉(zhuǎn)換成int類型
  • 最后我們修改while循環(huán)中的講數(shù)據(jù)添加到m_ArrayData的語句,這里我們調(diào)用Split方法,將數(shù)據(jù)以 “,” 作為分隔符,切割數(shù)據(jù)

寫完代碼,保存一下,然后我打開FileController腳本,修改代碼如下:

完整的代碼
  • 首先讀取文件的語句和之前一樣
  • 接著我們調(diào)用getString方法打印行數(shù)為1,列數(shù)為1的數(shù)據(jù)
  • 最后我們調(diào)用getInt方法打印行數(shù)為1,列數(shù)2的數(shù)據(jù)

寫完代碼,保存一下,大家可以先思考一下,上面打印的兩個(gè)數(shù)據(jù)分別是上面內(nèi)容,然后在運(yùn)行游戲,檢查一下實(shí)際結(jié)果是否和你想象的一樣

具體對(duì)應(yīng)的數(shù)據(jù)
打印結(jié)果

擴(kuò)展閱讀

前面我們提到過,關(guān)于文件路徑的存放位置,在本例中我們使用了Application.dataPath來指定路徑位置,關(guān)于路徑有4個(gè)類型:

  • Application.dataPath:該路徑指向我們Unity編輯器的Asset文件夾
  • Application.persistentDataPath:該路徑指向iOS和Android的沙盒路徑
  • Application.streamingAssetsPathstreamingAsset文件夾路徑,在任何平臺(tái)都可以通過這個(gè)路徑讀取到文件夾里的內(nèi)容
  • Application.temporaryCachePath:臨時(shí)數(shù)據(jù)文件路徑

關(guān)于這4種路徑的詳細(xì)地址,大家可以試著在Unity里面用Debug.Log語句將其打印出來,看到完整的路徑會(huì)便于理解

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容