
前言
在游戲制作中,我們的許多數(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è)文件

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

這就簡(jiǎn)單的制作了一個(gè)裝備數(shù)據(jù)表,每件裝備我們假設(shè)它有5個(gè)屬性:
- id:裝備ID,具有唯一性
- name:裝備的名字
- level:裝備等級(jí)
- attack:裝備增加的攻擊
- def:裝備增加的防御
數(shù)據(jù)填寫完之后,我們就可以選擇文件->導(dǎo)出到->CSV…

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

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

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

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

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

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

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

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

- 首先,如果我們要使用Unity的讀取文件的方法,需要在開頭引入System.IO的命名空間,這樣編輯器才會(huì)識(shí)別讀取文件的一些方法
- 然后我們還需要使用List<T>類型的變量,所以還要引入System.Collections.Generic的命名空間
- 然后這個(gè)CSV類是我們自己定義的一個(gè)靜態(tài)類,不需要繼承MonoBehaviour類,所以我們把繼承MonoBehaviour類的語句刪除掉
- 接著我們定義了csv和m_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腳本,輸入下面的代碼:

- 首先我們?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ù):

可以看到,現(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ù)了,先直接上代碼:

- 由于這次我們需要將每一行的數(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é)果是否和你想象的一樣


擴(kuò)展閱讀
前面我們提到過,關(guān)于文件路徑的存放位置,在本例中我們使用了Application.dataPath來指定路徑位置,關(guān)于路徑有4個(gè)類型:
- Application.dataPath:該路徑指向我們Unity編輯器的Asset文件夾
- Application.persistentDataPath:該路徑指向iOS和Android的沙盒路徑
- Application.streamingAssetsPath:streamingAsset文件夾路徑,在任何平臺(tái)都可以通過這個(gè)路徑讀取到文件夾里的內(nèi)容
- Application.temporaryCachePath:臨時(shí)數(shù)據(jù)文件路徑
關(guān)于這4種路徑的詳細(xì)地址,大家可以試著在Unity里面用Debug.Log語句將其打印出來,看到完整的路徑會(huì)便于理解