最近在工作上的專案因為使用了多國語系的功能,所以需要處理大量文字,但同時也造成了遊戲初始開啟時,卡偵的情況很嚴重。所以需要好好思考一下,要怎麼優化這個小地方。
規格是採用.csv ,用逗號分割字串,並含有三個語系。
(這邊是用Fungus的Localization系統)
Key | Description | Chinese | China | English |
.初次碰到的問題
原有的Fungus套件裡的Localization 系統,存在一個嚴重的問題,每當切換語系的時候,他會重新載入文字,並只取對應的語言儲到 Dictionary< Key, Value > 裡,量小的時候還好,但是當文字量達到上萬筆時,每次的切換都是一陣等待。
文字解析的步驟
.取得Csv全部的文字
.文字做Split("\r\n") 後,存成LineArray
.各LineArray再 Split(','),以逗號分割存成 InfoArray 取出所需資料
.各LineArray再 Split(','),以逗號分割存成 InfoArray 取出所需資料
.InfoArray[0] 必為 Key值
.截取索引的欄位的值存成Value
.回存到字典 Dictionary.Add(Key , Value);
// 原先的結構
// 原先的結構
protected static Dictionary<string string=""> localizedStrings = new Dictionary<string string="">();
public static GetLocalization(string Key) { if (localizedStrings.Contanins(Key)) return localizedString[Key]; }
原本的執行速度
因此我在去年改寫過一次,將資料結構改成 Dictionary < Key, Value[]> ,並記錄最後使用的語系索引值,雖然佔的內存變大了,而且整體速度沒變,但是之後切語系都是順暢的。
文字解析的步驟
.取得文字
.文字做Split("\r\n") 後,存成LineArray
.各LineArray再 Split(','),以逗號分割存成 InfoArray 取出所需資料
.InfoArray[0] 必為 Key值
.將取出的 InfoArray當做Value[] 存在字典裡.各LineArray再 Split(','),以逗號分割存成 InfoArray 取出所需資料
.InfoArray[0] 必為 Key值
.回存到字典 Dictionary.Add(Key , Value[]);
// 變更後的結構
protected static Dictionary<string, string[]> localizedStrings = new Dictionary<string, string[]>(); protected int LanguageIndex = 0; //取得內文 public static GetLocalization(string Key) { if (localizedStrings.Contanins(Key)) { if (localizedString.Length > LanguageIndex) return localizedString[Key][LanguageIndex]; } return Key; }
.後來隨著專案變的更大,資料量更多時,初次載入的時候已造成影響…
雖然解決了後續轉換語言的問題,但因為內容變的更多了,初次載入已造成嚴重的影響,所以今天試著改寫一下一開始解析的架構,並加上一些小優化。
*這邊造成的影響是因為做Split時,程式語言其實是用IndexOf、SubString 等去查找我們給的字元,因此現在我們的內文有高達500多萬字元時…其執行效率是非常差的。
/// 多國語系記錄類別
public class LocalizationInfo { public string BaseString; private string[] LocalizationInfos; public LocalizationInfo (string BaseString) { this.BaseString = BaseString; } public string GetLocalization(int CodeIndex , string DefaultString) { if (LocalizationInfos == null || LocalizationInfos.Length < 1) LocalizationInfos = BaseString.Split(','); if (LocalizationInfos.Length > CodeIndex) return LocalizationInfos[CodeIndex]; return DefaultString; }
}
這邊加了一個類別,我想把Split的次數減少,以目前現有專案2萬筆,每筆5個逗號,光一行就要SubString *5次 + IndexOf *5 次, 2萬筆下來可就數十萬次,而且估計每次SubString 都是從原本的本文Index 0 開始算起… 這開銷真的蠻可怕的。
(有錯請糾正,謝謝)
因此目前打算把執行次數壓到總行數,先把資料準備好,所以這邊的建構子傳入的是該"單行全部的內容"
待要用到的時候才進行Split 分割至預存的 Array[] 裡…畢竟多國語系2萬多筆,玩家玩一次可能才看的到幾十筆、幾百筆也說不一定。
*這裡甚至可以考慮加入一段時間後,把Array的記憶體釋放出來。
// 修改的結構
protected static Dictionary<string, LocalizationInfo> localizedStrings = new Dictionary<string, LocalizationInfo>();
//解析部份
public static void InitLocalization(TextAsset localizationFile) { string[] fLines = Regex.Split(localizationFile.text, "\r\n"); int TotalLines = fLines.Length; int LineCount = 0; string CurrentLineString = null; string TempKey = ""; do { CurrentLineString = null; CurrentLineString = fLines[LineCount]; if (LineCount == 0) { // Key欄的資料 LanguageCode = CurrentLineString.Split(','); } try { TempKey = CurrentLineString.Substring(0, CurrentLineString.IndexOf(',')); if (localizedStrings.ContainsKey(TempKey) == false) { localizedStrings.Add(TempKey, new LocalizationInfo(CurrentLineString)); } } catch { Debug.Error(string.Format("%u7B2C{0}%u884C%u6709%u554F%u984C-> {1}",LineCount , CurrentLineString)); } LineCount ; } while (LineCount < TotalLines);}
這邊解析的部份直接改寫,用正規表達式截取出一行一行。
由於多國語系第一行必為Key欄,所以直接在LineCount = 0 時做處理,其餘資料被建構成LocalizationInfo。
所以這邊的流程變為
.取得文字
.文字做正規表達式後,取出單行
.將單行存於 LocalizationInfo 的基本文字
.Key值為 該行文字.Substring(0, 該行文字.IndexOf(','));
.回存至字典 Dictionary.Add(Key , new LocalizationInfo(該行文字));
.要使用時,在去做細部Split
.將單行存於 LocalizationInfo 的基本文字
.Key值為 該行文字.Substring(0, 該行文字.IndexOf(','));
.回存至字典 Dictionary.Add(Key , new LocalizationInfo(該行文字));
.要使用時,在去做細部Split
//取得內文
public static GetLocalization(string Key) { if (localizedStrings.Contanins(Key)) { if (localizedString.Length > LanguageIndex) return localizedStrings[Key].GetLocalization(LanguageIndex,Key); } return Key; }
調整後的執行速度
以上是此次修正的方向,雖然內存可能變多了,但初始的速度整體變快許多。
======================================================================
後記:
其實對於處理大量文字一定還有更好的作法…看看搜尋引擎…不過我就沒有太深入,直到下次這個方法又碰到瓶頸,我才會去想辦法解決吧?
如果有人想直接留言給我關鍵字的話也非常歡迎~(不如說 拜託,請教我 QQ )
這個工具雖然是用Fungus套件改的,不過基本上把上面的架構拆出去…正規表達式規則自定義,Split 規則自字義,應該是有潛力變成一個共用形式的Class 類吧?
不過我的精神止於解決瓶頸…XD,接下來還是繼續寫遊戲的新機制吧,對我來說比較有趣 😄
Unity Fungus 套件
官網:http://fungusgames.com/
Asset Store:https://www.assetstore.unity3d.com/en/#!/content/34184
沒有留言:
張貼留言