60.自定义协议工具生成枚举

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

×

喜欢就点赞,疼爱就打赏