Unity 中操作数据文件

路径

路径定义

Resource

Resources文件夹是Unity里自动识别的一种文件夹,可在Unity编辑器的Project窗口里创建,并将资源放置在里面。Resources文件夹下的资源不管是否有用,全部会打包进.apk或者.ipa,并且打包时会将里面的资源压缩处理。加载方法是Resources.Load(文件名),需要注意:文件名不包括扩展名,打包后不能更改Resources下的资源内容,但是从Resources文件夹中加载出来的资源可以更改。

Application.dataPath

这个属性返回的是程序的数据文件所在文件夹的路径,例如在Editor中就是项目的Assets文件夹的路径,通过这个路径可以访问项目中任何文件夹中的资源,但是在移动端它是完全没用

Application.streamingAssetPath

这个属性用于返回流数据的缓存目录,返回路径为相对路径,适合设置一些外部数据文件的路径。在Unity工程的Assets目录下起一个名为“StreamingAssets”的文件夹即可,然后用Application.streamingAssetsPath访问,这个文件夹中的资源在打包时会原封不动的打包进去,不会压缩,一般放置一些资源数据。在PC/MAC中可实现对文件的“增删改查”等操作,但在移动端是一个只读路径

Application.persistentDataPath(推荐使用)

此属性返回一个持久化数据存储目录的路径,可以在此路径下存储一些持久化的数据文件。这个路径可读、可写,但是只能在程序运行时才能读写操作,不能提前将数据放入这个路径。在IOS上是应用程序的沙盒,可以被iCloud自动备份,可以通过同步推送一类的助手直接取出文件;在Android上的位置是根据Project Setting里设置的Write Access路径可以设置是程序沙盒还是sdcard,注意:如果在Android设置保存在沙盒中,那么就必须root以后才能用电脑取出文件,因此建议写入sdcard里。一般情况下,建议将获得的文件保存在这个路径下,例如可以从StreamingAsset中读取的二进制文件或者从AssetBundle读取的文件写入PersistentDatapath。

Application.temporaryCatchPath

此属性返回一个临时数据的缓存目录,跟Application.persistentDataPath类似,但是在IOS上不能被自动备份。

/sdcard/…

表示Android手机的SD卡根目录。

/storage/emulated/0/…

表示Android手机的内置存储根目录。

路径访问

以上各路径中的资源加载方式都可以用WWW类加载,但要注意各个平台路径需要加的访问名称,例如Android平台的路径前要加"jar:file://",其他平台使用"file://"。以下是各路径在各平台中的具体位置信息:

路径目录

Android平台

  • Application.dataPath : /data/app/xxx.xxx.xxx.apk
  • Application.streamingAssetsPath : jar:file:///data/app/xxx.xxx.xxx.apk/!/assets
  • Application.persistentDataPath : /data/data/xxx.xxx.xxx/files
  • Application.temporaryCachePath : /data/data/xxx.xxx.xxx/cache

IOS平台

  • Application.dataPath : Application/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/xxx.app/Data
  • Application.streamingAssetsPath : Application/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/xxx.app/Data/Raw
  • Application.persistentDataPath : Application/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/Documents
  • Application.temporaryCachePath : Application/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/Library/Caches

从以上结论可知,安卓平台下的Application.streamingAssetsPath无需增加jar:file://字符串。

实践应用

StreamingAssets文件夹下的只读不可写路径:

以Application.streamingAssetsPath为基准 : jar:file:///data/app/xxx.xxx.xxx.apk/!/assets

  • 安卓读:
    • filePath = Application.streamingAssetsPath + “/文件名.格式名”;
    • filePath = “jar:file://” + Application.dataPath + “/!/assets” + “/文件名.格式名”;
  • IOS读:
    • filePath = “file://” + Application.streamingAssetsPath + “/文件名.格式名”;
    • filePath = “file://” + Application.dataPath + “/Raw” + “/文件名.格式名”;
    • PC读:filePath = “file://” + Application.streamingAssetsPath + “/文件名.格式名”;/
Unity内部的可读可写路径:
  • 安卓: path = Application.persistentDataPath + “/文件名.格式名”;
  • IOS: path = Application.persistentDataPath + “/文件名.格式名”; //未验证
  • PC: path = “file://” + Application.persistentDataPath + “/文件名.格式名”;//

文件读写

TextAsset

文本类型文件,导入到Unity后,会被识别成TextAsset,包括:读取:.txt, .html, .htm, .xml, .json, .csv, .yaml, .fnt, .bytes。其中,.bytes是二进制类型的文件。

  • Resources.Load(string assetName)
  • AssetBundle.LoadAsset(string assetName)

可以如下加载并使用:

TextAsset data = Resoures.Load("name") as TextAsset;
// 如果是文本类型
string strContent = data.text;
// 如果是二进制类型
byte[] byteContent = data.bytes;

加载后,就可以根据特定的格式进行解析,使用了。

C# File

可以通过File类来直接读写文件,常用接口:

  • File.ReadAllText()
  • File.WriteAllText()
string path = Path.Combine(Application.persistentDataPath, "text.txt");
// 读文件
string fileContent = File.ReadAllText(path);
// 写文件
File.WriteAllText(path, fileContent);

需要注意:

Resources目录:Resources.Load(“test.txt”)(只读)

StreamingAsset目录:File.ReadAllText(Path.Combine(Application.streamingAssetPath, “test.txt”));(只读)

PersistentDataPath目录:File.ReadAllText(Path.Combine(Application.persistentDataPath, “test.txt”));(可读可写)

PlayerPrefs

Unity 提供了 PlayerPrefs 来方便地对玩家数据进行存档。

存储路径

  • macOS:~/Library/Preferences/unity.[company name].[product name].plist。
  • Windows:存储在注册表项中,HKCU\Software\ [company name]\ [product name] 键下。
  • Linux:~/.config/unity3d/[CompanyName]/[ProductName]
  • Windows Store Apps:%userprofile%\AppData\Local\Packages[ProductPackageId]>\LocalState\playerprefs.dat
  • Android:/data/data/pkg-name/shared_prefs/pkg-name.xml
  • iOS:/Library/Preferences/[bundle identifier].plist

读写数据接口

  • DeleteAll

    public static void DeleteAll();

    删除所有数据

  • DeleteKey

    public static void DeleteKey(string key);

    删除指定地Key的数据

  • SetFloatSetIntSetString

    public static void SetString(string key, string value);

    设置某个Key的值,支持float,int,string

  • HasKey

    public static bool HasKey(string key);

    检查key是否有有效的值

  • Save

    public static void Save();

    保存数据。执行OnApplicationQuit()时,会自动保存,但是我们可以在需要时调用Save立即保存。

  • GetFloatGetIntGetString

    public static string GetString(string key);

    public static string GetString(string key, string defaultValue);

    获取指定key的数据。可以传入一个默认数据,当key不存在时,返回默认数据。

应用

我们在使用时,通常会根据需要,定义存档数据结构,并将其转换成json字符串,进行存取。

EditorPrefs

EditorPrefs的接口与PlayerPrefs接口相同,只不过只能用在编辑器中,用来存储编辑器数据。

JSON

json是一种用字符串存储数据的格式。Unity提供了对json的支持。需要为序列化类,添加**[Serializable]**属性。

  • ToJson

    public static string ToJson(object obj);

    public static string ToJson(object obj, bool prettyPrint);

    将一个对象,序列化成josn字符串。prettyPrint=true,输出成以读格式,否则输出成最小尺寸的字符串。

  • FromJson

    public static T FromJson(string json);

    从json字符串反序列化出对象。

  • FromJsonOverwrite

    public static void FromJsonOverwrite(string json, object objectToOverwrite);

    从json字符串加载对象数据

例子

public class Data
{
    public int id;
    public List<SubData> scores = new List<SubData>();
}
public class SubData
{
    public string name;
    public int score;
}

Data d = new Data();
// 将对象序列化成json字符串
string json = JsonUtility.ToJson(d);
// 从json字符串反序列化出对象
Data d2 = JsonUtility.FromJson<Data>(json);
// 从json字符串中更新对象数据
JsonUtility.FromJsonOverride(d, json);

Json是无法支持字典类型的,所以我们只能将字典类型转化为2个List类型来进行存储,序列化和反序列化后手动处理:

public class DictionaryEx<K,V> : Dicionary<K,V>, ISerializationCallbackReceiver
{
	[SerializeField]
	private List<K> allKeys = new List<K>();
	[SerializeField]
	private List<V> allValues = new List<V>();
	public void OnAfterDeserialize()
	{
		for(int i = 0; i < allKeys.count; ++i)
			Add(allKeys[i], allValues[i])
	}
	public void OnBeforeSerialize()
	{
		foreach(var item in this)
		{
			allKeys.Add(item.Key);
			allValues.Add(item.Value);
		}
	}
}

Excel

游戏运行中,极少会对excel文件进行读写,多数情况下,Excel是策划编辑和配置游戏运行数据用的。而由于直接读取excel文件效率较低,而且文件尺寸较大,所以开发过程中,都是把excel文件导成其它格式,如csv,json。

可以使用EPPlus在Unity编辑器内直接操作Excel。

可以在https://github.com/JanKallman/EPPlus获取源代码使用。

使用例子

public class ExcelExample
{
    public void Func()
    {
        string filePath = Path.Combine(Application.persistentDataPath,"test.xlsx");
        // 创建并写文件
        File file = new FileInfo(filePath);
        using(ExcelPackage excel = new ExcelPackage(file))
        {
            ExcelWorksheet worksheet = excel.Workbok.Worksheets.Add("sheet1");
            worksheet[1,1].value = "aaaa";
            worksheet[1,2].value = "bbbb";
            excel.save();
        }
        AssetDatabase.Refresh();
        // 读取文件
        using(FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
        {
            using(ExcelPackage excel = new ExcelPackage(fs))
            {
                ExcelWorksheets worksheets = excel.WorkBook.Worksheets;
                //遍历所有工作表
                for(int i = 0; i < worsheets.Count; ++i)
                {
                    ExcellWorksheet sheet = worksheets[i];
                    int colCount = sheet.Dimension.End.Column;
                    string sheetName = sheet.Name;	// sheet名字
                    for(int row = 1, count = sheet.Dimension.End.Row; row <= count; ++row)
                    {
                        for(int col = 1; col <= colCount; ++col)
                        {
                            // 读取单元格中的数据
                            string text = sheet.Cells[row, col].Text ??"";
                        }
                    }
                }
            }
        }
    }
}

将策划的数据表,导出成游戏用的数据是开发工作流中非常重要的以环,如果该流程功能实现的好,可以避免很多错误,尤其是由于表格错误导致的游戏表现异常。因为这些异常,在未知原因的情况下,都是我们程序来查的,而且多数时候会发现不是代码逻辑问题,所以导表时提前发现错误,可以大幅降低我们的工作量。而且可以避免游戏上线后,由于数据错误导致的运营事故。

为了能导出,以及检查字段有效性,需要为字段增加额外行,大体设计如下:

1行:字段名字,不导出,中文,主要时策划方便,定义这些字段的含义。

2行:字段导出类型,导出,用来定义如何进行解析。比如int,bool,string,float,[int] 数组List,对象类型

3行:有效性检查。range(a,b)范围检查, textLength(4)字符串长度检查, reference(excel,sheet,col)关联表检查,等,还可以根据自己的项目进行自定义

4行:国际化[G]

5行:英文列名,索引名

如果表头用5行太多,其实可以将2,3,4行进行合并,用多个功能标识来定义不同属性。

根据功能标识,我们可以自动生成表格读取和查询的代码。这种自动化,就可以定义为工作流了。

XML

XML是以标签的方式来组织数据结构,C#提供了创建,解析,修改,查询的方法。命名空间为:System.XML。下面直接以例子说明用法:

// 创建XmlDocument
XmlDocument xmlDoc = new XmlDocument();
XmlDeclaration xmlDecl = xmlDoc.CreateXmlDeclaration("1.0", "UFT-8", null);
xmlDoc.AppendChild(xmlDecl);
// 写入数据
XmlElement root = xmlDoc.CreateElement("root");
xmlDoc.AppendChild(root);
XmlElement group = xmlDoc.createElement("group");
root.AppendChild(group);
group.SetAttribute("name", "name");
group.SetAttribute("age", "5");
// 输出成xml字符串
string strXml = "";
using(StringWriter sw = new StringWrite())
{
    using(XmlTextWriter xmlTW = new XmlTextWriter(sw))
    {
        xmlDoc.WriteTo(xmlTW);
        xmlTW.Flush();
        strXml = xmlTW.ToString();
    }
}
// 保存到文件
string filePath = Path.Combine(Application.dataPath, "test.xml");
xmlDoc.Save(filePath);
AssetDatabase.Refresh();

// 从字符串加载xml
xmlDoc.LoadXml(strXml);
// 从文件加载
xmlDoc.Load(filePath);
XmlNodes root = xmlDoc.SelectSingleNode("root");
foreach(XmlNode node in root.ChildNodes)
{
    string name = node.Attribute["name"].Value;
    string age = node.Attribute["age"].Value;
    // 修改数据
    node.Attribute["age"] = "8";
}
xmlDoc.Save(filePath);

详细的用法,可以参考C# API

YAML

Unity编辑器使用YAML格式保存数据。当我们开发工具扩展编辑器功能是,会经常给这类文件打交道,这里也说一下。

YAML格式解析

使用YamlDotNet .Net类库来操作yaml文件,可以在Asset Store中免费下载。

要用YamlDotNet序列化和反序列化,对象的变量需要提供get set 接口。

例子

class Data
{
    public string name{get;set;}
    public int id{get;set;}
    public List<string> ll{get;set;}
}

Data d = new Data();
d.ll = new List<string>();
// 序列化
YamlDotNet.Serialization.Serializer se = new YamlDotNet.Serialization.Serializer();
string yamlStr = se.Serialize(data);
// 反序列化
YamlDotNet.Serialization.Deserializer dese = new YamlDotNet.Serialization.Deserializer();
Data data2 = dese.Deserialize<Data>(yamlStr);

CSV

CSV,是以逗号作为分隔符的文件格式,该文件格式解析容易,但是有些问题需要解决:

值中包括逗号,这会导致解析错误,可以将值,用引号包围起来,引号内的作为一个值解析。

更详细的描述参见:CSV百科

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值