基于Unity的高效配置表读取方案

发表于2017-03-16
评论3 4.5k浏览

Unity 的行为可以通过配置来指定,使用 XML 文件来配置 Unity,也可以从任何格式的文件或者其他数据源中加载配置信息,在做配置表文件配置时因为太大遇到了问题,下面就给大家介绍下基于unity的高效配置表读取方案。


基本设计

内存对齐

·                     [StructLayoutAttribute(LayoutKind.Sequential, Pack = 1)]

·                     [MarshalAs(UnmanagedType.ByValArray,SizeConst=ResMacros.C_PRE_QUEST_SIZE)]

·                     string使用intkey,使用时,去查找,而不是在ResSturct里保存一个string指针

1
2
3
[MarshalAs(UnmanagedType.ByValArray, SizeConst=ResMacros.C_QUEST_REQUEST_MAX_COUNT)]
private ResQuestRequest[] _request;
private int _commitTraceText_key;

直接读取,而不是使用反射

1
2
3
4
5
6
7
8
9
10
11
12
13
byte[] bytes = new byte[rowSize];
Type type = typeof(T);
IntPtr structPtr = Marshal.AllocHGlobal(rowSize);
 
for (int i = 0; i < rowCount; i++)
{
    ms.Read(bytes, 0, rowSize);
    Marshal.Copy(bytes, 0, structPtr, rowSize);
    object _obj = Marshal.PtrToStructure(structPtr, type);
    objs[i] = (T)_obj;
}
 
Marshal.FreeHGlobal(structPtr);

顶层数据,使用class而不是struct

引用,避免copy

好处:

·                     本身就是常量,不必引入copy的开销

·                     配置表数据结构很多都比较大,copy开销并不能忽视

·                     Box/UnBox

保持常量,避免被修改

访问控制,只有getter,没有setter

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
[StructLayoutAttribute(LayoutKind.Sequential, Pack = 1)]
 public struct ResMonsterGs : IResStruct
 {
     #region Fields
     private int _tid;
     private int _level;
     private int _gs;
     #endregion
 
     #region Getter Setter
     public int Tid
     {
         get
         {
             return this._tid;
         }
     }
     public int Level
     {
         get
         {
             return this._level;
         }
     }
     public int Gs
     {
         get
         {
             return this._gs;
         }
     }
     #endregion

Arrayconst控制

使用lambda而不是反射

1
LoadConfig"">("Table/res_monster_type", "Id");
1
LoadGenericKeyConfigint,="" axresmonstertype="">("Table/res_monster_type", (row)=>{return row.Id;});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
private void LoadConfig"">(/*string type, */string filePath, string keyName = "")
        {
            Type t = typeof(ResClass);//System.Type.GetType("Config."+type);
            object instance = Activator.CreateInstance(t);
            System.Reflection.MethodInfo method = t.GetMethod("Load");
            if (method != null)
            {
                ResStruct[] objs = BinaryLoader.Load(filePath);
                if (objs == null)
                {
                    Console.WriteLine(BinaryLoader.GetErrorInfo());
                    Debug.Log(BinaryLoader.GetErrorInfo());
                    return;
                }
                if (keyName.CompareTo("") == 0)
                    method.Invoke(instance, new object[] { objs });
                else
                    method.Invoke(instance, new object[] { objs, keyName });
 
                if (this.table.ContainsKey(typeof(ResClass)))
                {
                    this.table[typeof(ResClass)] = (ResClass)instance;
                }
                else
                {
                    this.table.Add(typeof(ResClass), (ResClass)instance);
                }
                System.Reflection.MethodInfo callback_method = t.GetMethod("OnLoad");
                if (callback_method != null)
                {
                    callback_method.Invoke(instance, new object[] { });
                }
            }
        }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private void LoadGenericKeyConfig"" resclass="">(string filePath, GenericGetKeyDelegate""> func)
            where ResClass : GenericKeyTable"">
            where ResStruct : IResStruct, new()
        {
            Type t = typeof(ResClass);
            ResClass instance = (ResClass)Activator.CreateInstance(t);
 
            ResStruct[] objs = BinaryLoader.Load(filePath);
            if (objs == null)
            {
                Debug.LogError(BinaryLoader.GetErrorInfo());
                return;
            }
 
            instance.Load(objs, func);
 
            this.tables[typeof(ResClass)] = instance;
 
            instance.OnLoad();
        }


查询接口

添加配置表里是否存在某项的查询接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public virtual bool HasRowByIndex(int index)
       {
           if (index < table.Count)
               return true;
 
           return false;
       }
 
       public virtual bool HasRowByKey(KeyType key)
       {
           if (table.ContainsKey(key))
               return true;
 
           return false;
       }


原来判断是否存在的方法:

1
2
3
ResCbeSpell pRes = pCbeTable.GetRowByKey(pSpell.CbeId);
if (pRes.Id != 0)
{

代码整理

HasRowByIndex, HasRowByKey

添加了查询表项是否存在的接口,要使用这个接口确认是否存在

default(struct) => null

由于改成了class,表中没有的话,会返回null

性能对比

内存GC

旧版:

新版:

对比结果:

 

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引