Unity配置文件封装
应用场景
最近做的几个项目,都需要从配置文件中读取一些配置,然后做一些处理。于是就有了读写XML配置文件的想法,最终实现了如下封装:
如何使用
- 初始化
void Config.Init( string path, string rootNodeName );
初始化配置文件系统
path:XML配置文件的路径。
rootNodename XML根节点的名称
例如:
Config.Init(Path.Combine(Application.streamingAssetsPath, "config.xml"), "Demo");
- 获取属性
T Config.GetAttribute<T>( string node, string attr, T def );
获取节点属性
node:节点名(可以带路径)
attr:属性名
def:当获取失败时,返回一个默认的值。
例如:
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSplashScreen)]
private static void SetScreenResolution()
{
Config.Init(Path.Combine(Application.streamingAssetsPath, "config.xml"), "Demo");
int w = Config.GetAttribute<int>("Screen", "width", 1920 );
int h = Config.GetAttribute<int>("Screen", "height", 1080 );
bool f = Config.GetAttribute<bool>("Screen", "fullScreen", true);
Screen.SetResolution(w, h, f);
}
- 获取节点,获取属性(2)
有时候,需要连续读取同一个节点下的多个属性,比如上述设置分辨率的例子中,连续读取了同一个Screen节点的width、height、fullScreen等多个不同属性。这时候,就可以使用下面的API。
XmlElement Config.GetNode( string nodePath, bool bIsCreate=false );
nodePath指定要获取的节点路径,比如:“Screen”,或更深的路径:“Setup/Comm/Screen”。
bIsCreate表示如果不存在指定的路径,则尝试自动创建它。
T GetAttribute<T>( this XmlElement node, string attr, T def );
节点获取属性的扩展
例如:
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSplashScreen)]
private static void SetScreenResolution()
{
Config.Init(Path.Combine(Application.streamingAssetsPath, "config.xml"), "Demo");
XmlElement node = Config.GetNode("Screen");
if( node != null )
{
int w = node.GetAttribute<int>("width", 1080);
int h = node.GetAttribute<int>("height", 1920);
bool f = node.GetAttribute<bool>("fullScreen", true);
Screen.SetResolution(w, h, f);
}
}
- 设置节点属性
void Config.SetAttribute( string node, string attr, string val );
void SetAttribute( this XmlElement node, string attr, string val );
有时候不仅仅需要读取配置文件,有时候还需要自动的修改配置文件,那就需要有设置属性的方法。
5. 访问InnerText
XmlElement node = Config.GetNode("Content/Pages");
if( node != null )
UIText.text = node.InnerText;
- 保存
Config.Save( bool bForce = false );
bForce表示是否强制保存。默认为false,即只有配置文件内容被改变时,才会保存,否则什么也不做。当bForce被设置为true时,即便内容没有改变,仍然强制保存。
- 枚举子节点:
下面是枚举子节点并加载节点配置的简单例子(加载动态滚动图):
private void Start()
{
XmlElement node = Config.GetNode("Content");
if( node != null )
{
Period = node.GetAttribute<float>("period", 10f);
ScrollRect.ItemSpacing = node.GetAttribute<float>("spacing", 0);
ScrollRect.SpacingColor = node.GetAttribute<Color>("color", Color.black);
ScrollRect.Rate = Mathf.Clamp(node.GetAttribute<float>("rate", 0.15f), 0f, 1f);
ScrollRect.Slide = node.GetAttribute<float>("slide", 2f);
foreach( XmlNode child in node.ChildNodes )
{
if( child.NodeType == XmlNodeType.Element && child is XmlElement ele )
{
if (string.Compare(ele.Name, "Page", true) != 0)
continue;
string url = ele.GetAttributeString("url" );
if( string.IsNullOrWhiteSpace(url) || (!File.Exists(url)))
continue;
CarouselItem item = Instantiate<CarouselItem>(itemPrefab, ScrollRect.Content);
item.url = url;
}
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<Demo>
<!--屏幕配置-->
<Screen width="1080" height="1920" fullScreen="true" />
<!--内容配置
rate 滚动速度系数,0-1之间,越大速度越快
slide 滑动速度系数(不等于0)
spacing 分割条宽度
color 分割条颜色
period 每张图片停留展示时间(对视频无效,视频需播放完成)
-->
<Content rate="0.15" slide="3.0" spacing="20" color="#08353F" period="6">
<Page url="e:/photos/100_0221.jpg" />
<Page url="e:/temp/back.mp4" />
<Page url="e:/temp/one.mp4" />
<Page url="e:/temp/photos/100_0373.jpg" />
</Content>
</Demo>
完整代码
using UnityEngine;
using System;
using System.IO;
using System.Xml;
namespace HXConfig
{
public static class Config
{
private static XmlElement m_root = null;
public static bool IsHasChanged { get; private set; } = false;
public static string Path { get; private set; } = null;
public static XmlElement CreateNode( string name )
{
if (m_root != null && m_root.ParentNode != null)
{
XmlDocument doc = m_root.ParentNode as XmlDocument;
return doc.CreateElement(name);
}
else
return null;
}
public static void Init( string path, string root = null )
{
if( m_root == null )
{
Path = path;
XmlDocument doc = new XmlDocument();
if ((!string.IsNullOrWhiteSpace(Path)) && File.Exists(Path))
{
doc.Load(Path);
m_root = doc.DocumentElement;
IsHasChanged = false;
}
else
{
XmlDeclaration xd = doc.CreateXmlDeclaration("1.0", "utf-8", null );
doc.AppendChild(xd);
if (string.IsNullOrWhiteSpace(root))
root = "root";
m_root = doc.CreateElement(root);
doc.AppendChild(m_root);
IsHasChanged = true;
}
}
}
public static void Save(bool bForce = false)
{
if (m_root != null && (!string.IsNullOrWhiteSpace(Path)) && m_root.ParentNode is XmlDocument doc && (bForce || IsHasChanged))
doc.Save(Path);
}
public static void SetAttribute( string path, string attr, string val )
{
if (GetNode(path, true) is XmlElement xe)
{
xe.SetAttribute(attr, val);
IsHasChanged = true;
}
}
public static XmlElement GetNode(string path, bool bCreate = false )
{
if( bCreate)
{
XmlDocument doc = m_root.ParentNode as XmlDocument;
XmlElement xe = m_root;
foreach( var p in path.Split('/'))
{
if (string.IsNullOrWhiteSpace(p))
continue;
if (xe.SelectSingleNode(p) is XmlElement c)
xe = c;
else
{
XmlElement n = doc.CreateElement(p);
xe.AppendChild(n);
xe = n;
}
}
return xe;
}
else
return m_root.SelectSingleNode(path) as XmlElement;
}
public static string GetAttribute( string node, string attr, string def = null )
{
XmlElement n = GetNode(node);
return (n == null) ? def : n.GetAttribute(attr);
}
public static T GetAttribute<T>(string node, string attr) where T : struct
{
XmlElement n = GetNode(node);
return ( n == null ) ? default(T) : n.GetAttribute<T>(attr);
}
public static T GetAttribute<T>(string node, string attr, T def ) where T : struct
{
XmlElement n = GetNode(node);
return (n == null) ? def : n.GetAttribute<T>(attr, def);
}
public static string GetAttributeString(this XmlElement node, string attr, string def = null)
{
return node.HasAttribute(attr) ? node.GetAttribute(attr) : def;
}
public static T GetAttribute<T>(this XmlElement node, string attr ) where T : struct
{
return GetAttribute<T>(node, attr, default);
}
public static T GetAttribute<T>(this XmlElement node, string attr, T def ) where T : struct
{
try
{
string s = node.GetAttribute(attr);
if (typeof(T) == typeof(Vector3))
return (T)ConventVector3(s, def);
else if (typeof(T) == typeof(Vector2))
return (T)ConventVector2(s, def);
else if (typeof(T) == typeof(Color))
return (T)ConventColor(s, def);
else
return (T)Convert.ChangeType(s, typeof(T));
}
catch
{
return def;
}
}
private static object ConventVector3( string s, object def )
{
string[] vs = s.Split(',');
if (vs.Length < 3)
return (Vector3)def;
float[] vf = new float[3];
for (int i = 0; i < 3; ++i)
{
if (float.TryParse(vs[i], out float t))
vf[i] = t;
else
return (Vector3)def;
}
return new Vector3(vf[0], vf[1], vf[2]);
}
private static object ConventVector2( string s, object def )
{
string[] vs = s.Split(',');
if (vs.Length < 2)
return (Vector3)def;
float[] vf = new float[2];
for (int i = 0; i < 2; ++i)
{
if (float.TryParse(vs[i], out float t))
vf[i] = t;
else
return (Vector2)def;
}
return new Vector2(vf[0], vf[1] );
}
private static object ConventColor( string s, object def )
{
if (ColorUtility.TryParseHtmlString(s, out Color c))
return c;
else
return (Color)(def);
}
}
}