一款遊戲在研發初期就需要考慮多語言的問題,否則後期在進行多國語言版本時就面臨着巨大的成本。鑑於之前頁遊的經驗,其它同事設計出讀取Excel的方式來管理所有的文字。但是我在使用中發現很致使的一個問題,當多人編輯一個Excel時,衝突了就很麻煩,解決起來的成本還蠻高的。
之後我想了一些辦法,例如搭建一個web站點,將所有的字符串 Key、Value保存到數據庫中,避免衝突,方便去查詢。但感覺還是太過麻煩,成本略高。然後就想到一個辦法,既然讀取一個Excel容易衝突,那我就弄多個文件,一個人編輯一個Excel,這樣總不會衝突了吧。然後添加 Key 的時候,先查找 Key是否存在,如果存在就提醒添加者。
這樣問題就變成從讀取單個文件變成遍歷一個文件夾下的文件。因爲Excel在打開時,會生成一個臨時文件並被佔用,所以不可以對它進行操作(如複製)。
using System; using UnityEditor; using UnityEditor.UI; using UnityEngine; using UnityEngine.UI; using System.IO; using OfficeOpenXml; using System.Collections.Generic; using System.Text.RegularExpressions; namespace xxxx { [InitializeOnLoad] public class StringsWatcher { static string stringsPath; static DateTime lastModifyTime; static string stringsFolderPath; static StringsWatcher() { stringsFolderPath = Path.GetDirectoryName(Application.dataPath); stringsFolderPath = Path.Combine(stringsFolderPath, "strings"); // 創建Strings文件夾 if (!Directory.Exists(stringsFolderPath)) { Directory.CreateDirectory(stringsFolderPath); } // stringsPath = Path.GetFullPath(Path.Combine(rootPath, "Strings.xlsx")); EditorApplication.update += Update; } static void Update() { if (EditorApplication.isPlaying || EditorApplication.isCompiling) return; //if (!File.Exists(stringsPath)) return; if (!Directory.Exists(stringsFolderPath)) return; DateTime time = Directory.GetLastWriteTime(stringsFolderPath); if (lastModifyTime == time) return; lastModifyTime = time; Debug.Log("Reloading " + stringsFolderPath + ", Time : " + time.ToString()); DateTime startTime = DateTime.Now; // ExcelPackage package = new ExcelPackage(new FileInfo(tempFile)); List<string> keys = new List<string>(); List<string> values = new List<string>(); // 遍歷 strings 目錄下的excel文件 DirectoryInfo folder = new DirectoryInfo(stringsFolderPath); foreach (FileInfo fileItem in folder.GetFiles()) { string strFileType = fileItem.Extension; // 如果是 excel 文件且不是臨時文件, 臨時文件以~$開關,讀取臨時文件會報錯誤:Invaliddataexception the file is not an valid package file if (new Regex(@"^[^~]+\.xlsx$").IsMatch(fileItem.Name.ToLower())) { string tempFile = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); File.Copy(fileItem.FullName, tempFile); //Debug.Log(fileItem.Name + ", " + tempFile); ExcelPackage package = new ExcelPackage(new FileInfo(tempFile)); ExcelWorksheet sheet = package.Workbook.Worksheets[1]; int rows = sheet.Dimension.Rows; for (int row = 2; row <= rows; row++) { object keyObj = sheet.Cells[row, 1].Value; object valueObj = sheet.Cells[row, 2].Value; // Valid key and value is null or not. if (keyObj == null) { Debug.LogError("Find Key is null. fileName : " + fileItem.Name + ", rowIndex : " + row); return; } if (valueObj == null) { Debug.LogError("Find Key is null. fileName : " + fileItem.Name + ", rowIndex : " + row); return; } // Find key is Exist or not if (keys.Find(x => x == keyObj.ToString()) != null) { Debug.LogError("Find Report Key. fileName : " + fileItem.Name + ", rowIndex : " + row); return; } string key = keyObj.ToString(); string value = valueObj.ToString(); keys.Add(key); values.Add(value); } // 每刪除一個文件大約多0.1秒 File.Delete(tempFile); } // 更新內存的數據,重新保存assets AssetDatabase.SaveAssets(); } DateTime endTime = DateTime.Now; Debug.Log("Rebuild string.assets time : " + (endTime - startTime).TotalSeconds + "s"); } } }
如果你讀取Excel時遇到了 Invaliddataexception the file is not an valid package file ,上面的代碼或許對你有所幫助。
除了文字外,遊戲項目中還需要管理的就是帶有文字的UI圖片,這個也需要提前進行約定,制定相關的規範。