Unity3d通用工具类之数据配置加载类

今天,我们来讲讲游戏中的数据配置加载。

什么是游戏数据加载呢?一般来说游戏中会有场景地图。

按照国际惯例,先贴一张游戏场景的地图:

在这张地图上,我们可以看到有很多正六边形,正六边形上有树木、岩石等。

哎!那么问题也就来了。大家会思考这张地图怎么啦。关游戏数据配置有什么关系?我们做好场景直接loding进来不就行了?

这也就是问题所在,如果你是直接loding进场景有很多问题:

1.场景是死的。只能是这个做好的场景。如果你想删除一些正六边形,想改变一些树木的位置,如何完成。有人会想,那我再做一个这样场景不就行了。ok,如果死策划想要另外,再另外外外。。。的场景,好了,你做到手抽筋都做不完。

如果是数据配置的话,我们只需要修改一些配置表上的一些数据就可以了,然后我们在程序里面动态的加载这个场景就行了。

2.不易游戏热更新。当我们想要改变游戏场景,我们只能重新下载做好的场景,然后替换掉游戏里面的文件。一个场景有多大,大家想想就知道了。

如果是数据配置的话,我们只需要更新xml的配置表就完全ok了。这两者的差别是一个天上一个地上的差别。

3.暂时想不出来。留给你们补充了。

ok,既然讲了数据配置这么多的好处。我们现在就来写写通用的数据配置加载。

按照国际惯例,在写之前我们需要设计这些类。

1.首先,要有数据管理类GameDataManager,管理这不同类型的数据,比如地图数据,tip提示字符串数据,特效数据等等

2.既然有管理类,肯定要有我们的GameData类。

先来看一张配置表的信息:

可以看到map有包括id,因为不止一张地图。埃!既然不止一张地图,所以我们就得用dictionary<int,>

一个map里面除了id之外还存放着许多不同类型变量的数值。所以我们需要类来存这些变量,也就是GameData,但是GameData只能是一个类型的。

比如你设计的GameData类里面有fog,fogColor这些字段来存储地图信息,但是这只能是地图,你特效这些数据怎么办,难道要创建一个特效EffectData。所以我们把GameData当成基类。

由于一个地图类,里面就分开好多不同的地图,所以我们用dictionary<int,T>泛型来存储,int=>id,T=>GameData

所以我们就需要设计一个泛型的GameData<T>类,而T是继承GameData<T>.可能有些乱,看看uml就清晰了一些:

using UnityEngine;
using System.Collections.Generic;
#region 模块信息
/*----------------------------------------------------------------
// 模块名:GameData
// 创建者:chen
// 修改者列表:
// 创建日期:2015.11.8
// 模块描述:数据配置类
//----------------------------------------------------------------*/
#endregion
public class GameData
{
	public int id;
	public static Dictionary<int,T> GetData<T>()
	{
        Dictionary<int, T> dataMap;
        var type = typeof(T);
        var fileNameField = type.GetField("fileName");//这里取得对应的xml文件名
        if (fileNameField != null)
        {
            string filename = fileNameField.GetValue(null) as string;
            dataMap = GameDataManager.Instance.FormatXMLData(filename, typeof(Dictionary<int, T>), type) as Dictionary<int, T>;
        }
        else
        {
            dataMap = new Dictionary<int, T>();
        }
        return dataMap;
	}
}
public class GameData<T> : GameData where T : GameData<T>
{
	private static Dictionary<int,T> m_data;
	public static Dictionary<int,T> Data
	{
		get
		{
			if (null == m_data)
			{
				m_data = GetData<T>();
			}
			return m_data;
		}
		set
		{
			m_data = value;
		}
	}
}

 GameDataManager:

using UnityEngine;
using System.Collections.Generic;
using System;
#region 模块信息
/*----------------------------------------------------------------
// 模块名:Gam
// 创建者:chen
// 修改者列表:
// 创建日期:#CREATIONDATE#
// 模块描述:数据加载类
//----------------------------------------------------------------*/
#endregion
public class GameDataManager
{
    private static GameDataManager m_instance;
    protected static readonly bool m_bIsPreloadData = true;
    protected readonly string m_resourcePath;
    public static GameDataManager Instance
    {
        get
        {
            return m_instance ?? new GameDataManager();//??合并运算符,只当运算符的左操作数不为 null,此运算符将返回左操作数;否则返回右操作数
        }
    }

    public object FormatXMLData(string filename,Type dicType,Type type)
    {
        filename = string.Concat(this.m_resourcePath, filename, ".xml");
        object result = null;
        try
        {
            result = dicType.GetConstructor(Type.EmptyTypes).Invoke(null);
            Dictionary<int, Dictionary<string, string>> map;
            if (XMLParser.LoadIntoMap(filename, out map))//为何不让类的负担太重,这里我吧加载xml成dictionary单独分成一个类来处理
            {
                var props = type.GetProperties();//取得类的所有属性
                foreach (var item in map)
                {
                    var t = type.GetConstructor(Type.EmptyTypes).Invoke(null);
                    foreach (var prop in props)
                    {
                        if (prop.Name.Equals("id"))
                        {
                            prop.SetValue(t, item.Key, null);//如果是id的话,就设置属性值
                        }
                        else
                        {
                            if (item.Value.ContainsKey(prop.Name))
                            {
                                var value = UnityTools.GetValue(item.Value[prop.Name], prop.PropertyType);//通用工具类,吧string的格式转成对应的数据类型
                                prop.SetValue(t, value, null);
                            }
                        }
                    }
                    dicType.GetMethod("Add").Invoke(result, new object[] { item.Key, t });//result是dicType的实例,也就是dic<int,T>
                    //item.key=>id,t是泛型T的实例
                }
            }
        }
        catch (Exception e)
        {
            Debug.LogError(e.ToString());
        }
        return result;
    }

}

XMLParser:

using UnityEngine;
using System.Collections.Generic;
using System;
using System.Xml;
#region 模块信息
/*----------------------------------------------------------------
// 模块名:XMLParser
// 创建者:chen
// 修改者列表:
// 创建日期:#CREATIONDATE#
// 模块描述:
//----------------------------------------------------------------*/
#endregion
public class XMLParser
{
    public static bool LoadIntoMap(string filename,out Dictionary<int,Dictionary<string,string>> map)
    {
        try
        {
            XmlDocument xml = new XmlDocument();
            xml.Load(filename);
            if (null == xml)
            {
                Debug.LogError("xml文件不存在" + filename);
                map = null;
                return false;
            }
            else
            {
                map = LoadIntoMap(xml, filename);
                return true;
            }
        }
        catch(Exception e)
        {
            Debug.LogError("XML加载出错:" + e.ToString());
            map = null;
            return false;
        }
    }
    public static Dictionary<int, Dictionary<string, string>> LoadIntoMap(XmlDocument doc,string filePath)
    {
        Dictionary<int, Dictionary<string, string>> result = new Dictionary<int, Dictionary<string, string>>();
        int index = 0;
        XmlNode root = doc.SelectSingleNode("root");
        foreach (XmlNode node in root.ChildNodes)//root的子节点,就拿map_setting来讲就是map子节点
        {
            index++;
            if (null == node.ChildNodes || 0 == node.ChildNodes.Count)
            {
                Debug.LogWarning("The XML is empty nodes");
                continue;
            }
            int key = int.Parse(node.ChildNodes[0].InnerText);//map[0] ==> id
            if (result.ContainsKey(key))
            {
                Debug.LogWarning(string.Format("key:{0} is already loaded", key));
                continue;
            }
            var children = new Dictionary<string, string>();
            result.Add(key, children);
            for (int i = 1; i < node.ChildNodes.Count; i++)//去除id,所以i从1开始(这样id节点得放在第一个位置)
            {
                var childNode = node.ChildNodes[i];
                string tag;
                if (childNode.Name.Length < 3)
                {
                    tag = childNode.Name;
                }
                else
                {
                    var tagTial = childNode.Name.Substring(childNode.Name.Length - 2, 2);//截取最后两个字符
                    if (tagTial == "_i" || tagTial == "_s" || tagTial == "_f" || tagTial == "_l" || tagTial == "k" || tagTial == "_m")
                    {
                        tag = childNode.Name.Substring(0, childNode.Name.Length - 2);
                    }
                    else
                    {
                        tag = childNode.Name;
                    }
                }
                if (childNode != null && !children.ContainsKey(tag))
                {
                    if (string.IsNullOrEmpty(childNode.InnerText))
                    {
                        children.Add(tag, "");
                    }
                    else
                    {
                        children.Add(tag, childNode.InnerText.Trim());
                    }
                }
                else
                {
                    Debug.LogWarning(string.Format("XML文件子节点的Key:{0} 已经存在",tag));
                }
            }
        }
        return result;
    }
}
UnityTools:
/// <summary>
        /// 将字符串转化成为对应类型的值
        /// </summary>
        /// <param name="value"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        public static object GetValue(string value, Type type)
        {
            if (null == type)
            {
                return null;
            }
            else if (type == typeof(int))
            {
                return Convert.ToInt32(value);
            }
            else if (type == typeof(float))
            {
                return float.Parse(value);
            }
            else if (type == typeof(byte))
            {
                return Convert.ToByte(value);
            }
            else if (type == typeof(double))
            {
                return Convert.ToDouble(value);
            }
            else if (type == typeof(bool))
            {
                if (value == "0")
                {
                    return false;
                }
                else if (value == "1")
                {
                    return true;
                }
            }
            return null;
        }

既然已经写好了,如何使用呢?很简单,我们只需一句代码:比如

GameData<MapData>.dataMap这样就行啦!!!!!

前提是写好MapData类。注意MapData里面都需要写的是属性。不是字段。



时间: 2024-12-11 17:30:13

Unity3d通用工具类之数据配置加载类的相关文章

PHP中的use、命名空间、引入类文件、自动加载类的理解

use只是使用了命名空间,但是要想调用类,必须要加载类文件,或者自动加载. 即便是引入了其中一个类,如果没有自动加载机制,还是会报错 use的几种用法 namespace Blog\Article; class Comment { } //创建一个BBS空间(我有打算开个论坛) namespace BBS; //导入一个命名空间 use Blog\Article; //导入命名空间后可使用限定名称调用元素 $article_comment = new Article\Comment(); //为

Java运行时动态加载类之ClassLoader

https://blog.csdn.net/fjssharpsword/article/details/64922083 *************************************************************************** 需求场景:动态加载类ClassLoaderd,在xml文件中配置加载类名称和方法,: 一.准备 1)在D:\\tmp\\目录下配置a.xml文件: <?xml version="1.0" encoding=&q

[javaSE] 反射-动态加载类

Class.forName(“类的全称”) ①不仅表示了类的类类型,还代表了动态加载类 ②请大家区分编译,运行 ③编译时刻加载类是静态加载类,运行时刻加载类是动态加载类 Ⅰ所有的new对象都是静态加载类 在编译的时刻就要去检测该类是否存在,如果不存在,编译失败. //对于这种情况,静态加载不适用,因为我们需要根据输入来确定加载哪个类 package com.tsh.reflect; class ReflectLoadDemo { public static void main(String[]

Java反射第二课 动态加载类

在第一节课中我们讲的第三种方法 Class.forName("类的全称"); 不仅表示了类的类类型,还代表了动态加载类 请大家区分编译,运行 编译时刻加载类是静态加载类,运行时刻加载类是动态加载类 现在的开发工具比如eclipse把编译过程给隐藏了 下面做一个实验 比如用记事本编写下面的Office.java文件 class Office { public static void main(String[] args) { if(("Word").equals(ar

Java--Reflect(反射)专题2——动态加载类

转载请注明:http://blog.csdn.net/uniquewonderq 首先区分什么是动态加载,什么是静态加载? Class.forName("类的全称");//第三种表达类类型的方式 不仅代表了类的类类型,还代表了动态加载类 要区分编译和运行. 编译时刻加载类是静态加载类.运行时刻加载类是动态加载类. 在编译时刻就需要加载所有的可能使用到的类. 演示代码如下:功能性的类,最好使用动态加载.如,更新,升级,增加新功能等. 首先是一个接口,用于动态加载的.稍候可体验出来. pa

java动态加载类和静态加载类笔记

JAVA中的静态加载类是编译时刻加载类  动态加载类指的是运行时刻加载类 二者有什么区别呢 举一个例子  现在我创建了一个类  实现的功能假设为通过传入的参数调用具体的类和方法 class office { public static void main(String args[]) { if("word".equals(args[0]) { word w=new word(); word.run(); } if("excel".equals(args[0]) {

Java 反射理解(二)-- 动态加载类

Java 反射理解(二)-- 动态加载类 概念 在获得类类型中,有一种方法是 Class.forName("类的全称"),有以下要点: 不仅表示了类的类类型,还代表了动态加载类 编译时刻加载类是静态加载类,运行时刻加载类是动态加载类 演示 我们以具体的代码来演示什么是动态加载类和静态加载类: 新建:Office.java: class Office { public static void main(String[] args) { // new 创建对象,是静态加载类,在编译时刻就需

jvm内存模型,java类从编译到加载到执行的过程,jvm内存分配过程

一.jvm内存模型 JVM 内存模型主要分为堆.程序计数器.方法区.虚拟机栈和本地方法栈 1.堆 1.1.堆是 JVM 内存中最大的一块内存空间. 1.2.该内存被所有线程共享,几乎所有对象和数组都被分配到了堆内存中. 1.3.堆被划分为新生代和老年代,新生代又被进一步划分为 Eden 和 Survivor 区,最后 Survivor 由 From Survivor 和 To Survivor 组成. 2.程序计数器(Program Counter Register) 程序计数器是一块很小的内存

静态、抽象类、加载类、魔术方法等

静态  static关键字 普通成员普通成员是属于对象的 静态成员静态成员是属于类的 普通方法里面可以调用静态成员静态方法里面不能调用普通成员self关键字 在类里面代表该类 普通类 class Ren { public $name="张三"; public static $zhongzu; //静态成员 普通方法 function Say() { echo self::$zhongzu."你好"; } 静态类 static function Run() { ech