60.消息处理-自定义协议生成工具-协议消息生成-生成枚举
60.1 知识点
协议消息生成主要做什么?
- 协议生成:主要是使用配置文件中读取出来的信息,动态生成对应语言的代码文件。
- 作用:每次添加消息或者数据结构类时,我们不需要再手写代码了。我们不仅可以生成 C# 脚本文件,还可以根据需求生成别的语言的文件。
制作功能前的准备工作
- 协议生成:不会在发布后使用的功能,主要是在开发时使用。
- 实现方式:在 Unity 中将其作为一个编辑器功能来实现,可以专门新建一个 Editor 文件夹(专门放编辑器相关内容,不会发布),在其中放置配置文件和自动生成相关脚本文件。
把上一节课的 xml 文件改名成 ProtocolInfo.xml 放到 Editor 文件夹下
创建 ProtocolTool 类,放在 Editor 文件夹下,类中定义 xml 配置文件所在的路径,加入特性使编辑器中出现生成脚本的按钮
public class ProtocolTool
{
// 配置文件所在路径
private static string PROTO_INFO_PATH = Application.dataPath + "/Editor/ProtocolTool/ProtocolInfo.xml";
[MenuItem("ProtocolTool/生成C#脚本")]
private static void GenerateCSharp()
{
Debug.Log("生成C#代码");
}
[MenuItem("ProtocolTool/生成C++脚本")]
private static void GenerateC()
{
Debug.Log("生成C++代码");
}
[MenuItem("ProtocolTool/生成Java脚本")]
private static void GenerateJava()
{
Debug.Log("生成Java代码");
}
}
制作生成枚举功能
ProtocolTool 类中定义读取 xml 相关信息的方法
/// <summary>
/// 获取指定名字的所有子节点的 List
/// </summary>
/// <param name="nodeName"></param>
/// <returns></returns>
private static XmlNodeList GetNodes(string nodeName)
{
XmlDocument xml = new XmlDocument();
xml.Load(PROTO_INFO_PATH);
XmlNode root = xml.SelectSingleNode("messages");
return root.SelectNodes(nodeName);
}
定义 GenerateCSharp 类,专门用于生成 C# 代码。定义协议保存路径。之后这个类会写生成枚举,数据结构类,消息类的逻辑。
public class GenerateCSharp
{
// 协议保存路径
private string SAVE_PATH = Application.dataPath + "/Scripts/Protocol/";
// 生成枚举
public void GenerateEnum(XmlNodeList nodes)
{
}
// 生成数据结构类
// 生成消息类
}
在 ProtocolTool 类定义静态 GenerateCSharp 类对象。生成 C# 脚本的方法中调用 GenerateCSharp 类对象生成枚举函数,传入所有的枚举 xml 节点列表。
private static GenerateCSharp generateCSharp = new GenerateCSharp();
[MenuItem("ProtocolTool/生成C#脚本")]
private static void GenerateCSharp()
{
// 读取 xml 相关的信息
// XmlNodeList list = GetNodes("enum");
// 根据这些信息 去拼接字符串 生成对应的脚本
generateCSharp.GenerateEnum(GetNodes("enum"));
// 刷新编辑器界面 让我们可以看到生成的内容 不需要手动进行刷新了
AssetDatabase.Refresh();
}
观察自己声明的枚举类应该怎么拼接,等一下照着拼
namespace GamePlayer
{
public enum E_PLAYER_TYPE
{
// 枚举字段
MAIN = 1,
OTHER,
}
}
根据枚举相关信息实现 GenerateCSharp 类的生成枚举函数,拼接字符串,生成枚举脚本文件
// 生成枚举
public void GenerateEnum(XmlNodeList nodes)
{
// 生成枚举脚本的逻辑
string namespaceStr = "";
string enumNameStr = "";
string fieldStr = "";
foreach (XmlNode enumNode in nodes)
{
// 获取命名空间配置信息
namespaceStr = enumNode.Attributes["namespace"].Value;
// 获取枚举名配置信息
enumNameStr = enumNode.Attributes["name"].Value;
// 获取所有的字段节点 然后进行字符串拼接
XmlNodeList enumFields = enumNode.SelectNodes("field");
// 一个新的枚举 需要清空一次上一次拼接的字段字符串
fieldStr = "";
foreach (XmlNode enumField in enumFields)
{
fieldStr += "\t\t" + enumField.Attributes["name"].Value;
if (enumField.InnerText != "")
fieldStr += " = " + enumField.InnerText;
fieldStr += ",\r\n";
}
// 对所有可变的内容进行拼接
string enumStr = $"namespace {namespaceStr}\r\n" +
"{{\r\n" +
$"\tpublic enum {enumNameStr}\r\n" +
"\t{\r\n" +
$"{fieldStr}" +
"\t}\r\n" +
"}";
// 保存文件的路径
string path = SAVE_PATH + namespaceStr + "/Enum/";
// 如果不存在这个文件夹 则创建
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
// 字符串保存 存储为枚举脚本文件
File.WriteAllText(path + enumNameStr + ".cs", enumStr);
}
Debug.Log("枚举生成结束");
}
点击编辑器按钮进行生成枚举类测试,可以修改 xml 文件后再次生成查看是否成功
<!-- 枚举配置规则 包含 枚举名 命名空间 枚举字段名 可能包含枚举字段值 -->
<enum name="E_PLAYER_TYPE" namespace="GamePlayer">
<field name="MAIN">1</field>
<field name="OTHER"/>
</enum>
<enum name="E_HERO_TYPE" namespace="GamePlayer">
<field name="MAIN"/>
<field name="OTHER"/>
</enum>
<enum name="E_MONSTER_TYPE" namespace="GameMonster">
<field name="NORMAL">2
</field>
<field name="BOSS"/>
</enum>
60.2 知识点代码
Lesson60_消息处理_自定义协议生成工具_协议消息生成_生成枚举
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Lesson60_消息处理_自定义协议生成工具_协议消息生成_生成枚举 : MonoBehaviour
{
void Start()
{
#region 知识点一 协议消息生成主要做什么?
//协议生成 主要是使用配置文件中读取出来的信息
//动态的生成对应语言的代码文件
//每次添加消息或者数据结构类时,我们不需要再手写代码了
//我们不仅可以生成C#脚本文件,还可以根据需求生成别的语言的文件
#endregion
#region 知识点二 制作功能前的准备工作
//协议生成是不会在发布后使用的功能,主要是在开发时使用
//所以我们在Unity当中可以把它作为一个编辑器功能来做
//因此我们可以专门新建一个Editor文件夹(专门放编辑器相关内容,不会发布)
//在其中放置配置文件、自动生成相关脚本文件
#endregion
#region 知识点三 制作生成枚举功能
//1.读取xml枚举相关信息
//2.根据枚举相关信息 拼接字符串
//3.生成枚举脚本文件
#endregion
#region 总结
//根据配置生成脚本的文件的主要思路就是
//按规则拼接字符串
//只要有数据和规则,我们就可以动态的创建脚本文件
#endregion
}
}
GenerateCSharp
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Xml;
using UnityEngine;
namespace GamePlayer//1
{
public enum E_PLAYER_TYPE//2
{
//3
MAIN = 1,
OTHER,
}
}
public class GenerateCSharp
{
//协议保存路径
private string SAVE_PATH = Application.dataPath + "/Scripts/Protocol/";
//生成枚举
public void GenerateEnum(XmlNodeList nodes)
{
//生成枚举脚本的逻辑
string namespaceStr = "";
string enumNameStr = "";
string fieldStr = "";
foreach (XmlNode enumNode in nodes)
{
//获取命名空间配置信息
namespaceStr = enumNode.Attributes["namespace"].Value;
//获取枚举名配置信息
enumNameStr = enumNode.Attributes["name"].Value;
//获取所有的字段节点 然后进行字符串拼接
XmlNodeList enumFields = enumNode.SelectNodes("field");
//一个新的枚举 需要清空一次上一次拼接的字段字符串
fieldStr = "";
foreach (XmlNode enumField in enumFields)
{
fieldStr += "\t\t" + enumField.Attributes["name"].Value;
if (enumField.InnerText != "")
fieldStr += " = " + enumField.InnerText;
fieldStr += ",\r\n";
}
//对所有可变的内容进行拼接
string enumStr = $"namespace {namespaceStr}\r\n" +
"{\r\n" +
$"\tpublic enum {enumNameStr}\r\n" +
"\t{\r\n" +
$"{fieldStr}" +
"\t}\r\n" +
"}";
//保存文件的路径
string path = SAVE_PATH + namespaceStr + "/Enum/";
//如果不存在这个文件夹 则创建
if (!Directory.Exists(path))
Directory.CreateDirectory(path);
//字符串保存 存储为枚举脚本文件
File.WriteAllText(path + enumNameStr + ".cs", enumStr);
}
Debug.Log("枚举生成结束");
}
//生成数据结构类
//生成消息类
}
ProtocolTool
public class ProtocolTool
{
//配置文件所在路径
private static string PROTO_INFO_PATH = Application.dataPath + "/Editor/ProtocolTool/ProtocolInfo.xml";
private static GenerateCSharp generateCSharp = new GenerateCSharp();
[MenuItem("ProtocolTool/生成C#脚本")]
private static void GenerateCSharp()
{
//1.读取xml相关的信息
//XmlNodeList list = GetNodes("enum");
//2.根据这些信息 去拼接字符串 生成对应的脚本
generateCSharp.GenerateEnum(GetNodes("enum"));
//刷新编辑器界面 让我们可以看到生成的内容 不需要手动进行刷新了
AssetDatabase.Refresh();
}
[MenuItem("ProtocolTool/生成C++脚本")]
private static void GenerateC()
{
Debug.Log("生成C++代码");
}
[MenuItem("ProtocolTool/生成Java脚本")]
private static void GenerateJava()
{
Debug.Log("生成Java代码");
}
/// <summary>
/// 获取指定名字的所有子节点 的 List
/// </summary>
/// <param name="nodeName"></param>
/// <returns></returns>
private static XmlNodeList GetNodes(string nodeName)
{
XmlDocument xml = new XmlDocument();
xml.Load(PROTO_INFO_PATH);
XmlNode root = xml.SelectSingleNode("messages");
return root.SelectNodes(nodeName);
}
}
ProtocolTool.xml
<?xml version="1.0" encoding="UTF-8"?>
<messages>
<!--枚举配置规则 包含 枚举名 命名空间 枚举字段名 可能包含枚举字段值-->
<enum name="E_PLAYER_TYPE" namespace="GamePlayer">
<field name="MAIN">1</field>
<field name="OTHER"/>
</enum>
<enum name="E_HERO_TYPE" namespace="GamePlayer">
<field name="MAIN"/>
<field name="OTHER"/>
</enum>
<enum name="E_MONSTER_TYPE" namespace="GameMonster">
<field name="NORMAL">2</field>
<field name="BOSS"/>
</enum>
<!--数据结构类配置规则 包含 类名 命名空间 变量类型 变量名-->
<data name="PlayerData" namespace="GamePlayer">
<field type="int" name="id"/>
<field type="float" name="atk"/>
<field type="bool" name="sex"/>
<field type="long" name="lev"/>
<field type="array" data="int" name="arrays"/>
<field type="list" T="int" name="list"/>
<field type="dic" Tkey="int" Tvalue="string" name="dic"/>
<field type="enum" data="E_HERO_TYPE" name="heroType"/>
</data>
<data name="HeartData" namespace="GameSystem">
<field type="long" name="time"/>
</data>
<!--消息类类配置规则 包含 消息ID 类名 命名空间 变量类型 变量名-->
<message id="1001" name="PlayerMessage" namespace="GamePlayer">
<field type="int" name="playerID"/>
<field type="PlayerData" name="data"/>
</message>
<message id="1002" name="HeartMessage" namespace="GameSystem"/>
</messages>
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com