首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 其他教程 > 其他相关 >

浅谈MMORPG任务编辑器的设计与兑现

2012-09-29 
浅谈MMORPG任务编辑器的设计与实现浅谈MMORPG任务编辑器的设计与实现By 马冬亮(凝霜Loki)一个人的战争(htt

浅谈MMORPG任务编辑器的设计与实现

浅谈MMORPG任务编辑器的设计与实现

By 马冬亮(凝霜  Loki)

一个人的战争(http://blog.csdn.net/MDL13412)

定义

        MMORPG任务编辑器用于配置人物与地图NPC、怪物、玩家、场景等相关的任务交互操作,处理诸如与NPC对话、杀死BOSS、收集物品等事件,并设置这些事件的响应过程及触发/完成条件等等。例如:接受设置玩家可以通过与地图上某NPC对话接受一个任务,杀死10个指定的怪物,那么任务编辑器就需要对任务的接受条件、完成条件、任务相关变量、自动寻路、杀死怪物后的提示信息等功能进行配置。

设计理念

        首先,任务编辑器是提供给策划人员使用的,而策划人员一般都不懂得编程,所以其设计应遵循WYSIWYG(What You See Is What You Get ,所见即所得)的原则,做到傻瓜化;其次,编辑器应该具有按地图过滤任务、搜索指定任务、任务描述彩色显示、任务变量超链接样式显示(点击超链接时弹出编辑对话框)、插入自动寻路超链接(可以弹出的对话框中手动填写或按地图选择指定NPC)等功能;再次,任务编辑器的布局应该按照功能来分配,尽量做到符合用户的思维习惯,做到“最小惊奇”原则;最后,任务编辑器应该预留足够的扩展性,以满足游戏玩法的扩展。

带来的优势

        通过WYSIWYG的设计理念,可以最大程度的减少策划的心智负担,从而减少其出错的可能性,减少与程序员沟通、调试的成本,显著的提升了团队写作效率;通过提供任务搜索的功能,可以加速策划定位到相应的任务,完成修改、删除等操作,提升策划工作效率;编辑器可以实现源码级别上的复用,公司新开发一个MMORPG,那么只需要在原有的任务编辑器基础上增加、删除功能即可,降低总体拥有成本。

任务响应机制设计

        MMORPG具有强交互的特性,玩家的每一个操作都可以看作是一种特定类型的事件,因此基于消息的事件响应模型非常适合完成此任务。例如:玩家进入场景,可以抽象为on_enter事件,并且客户端向服务器发送on_enter事件,服务器端将其压入消息队列,并进行处理,而后返回处理结果。

任务脚本结构设计

        任务编辑器是对任务脚本的抽象,将其细节隐藏,而脚本最终还是需要由程序处理,因此必须仔细进行设计。这里给出的是国内某著名网络游戏的脚本设计,其任务脚本都包含在quest文件夹中,所有通过玩家任务列表查看的任务都保存在quests.xml中,而相应的事件,则保存在on_drop、on_enter、on_get这些事件文件夹中,事件文件夹中的文件是以NPC_ID进行命名,以此来配置不同NPC对不同事件的响应操作,如下图所示:

浅谈MMORPG任务编辑器的设计与兑现

        首先,我们来看quests.xml文件中的数据结构:

关键技术点--RichTextBox字体着色

        字体着色非常简单,这里用到了一个RichTextBoxLinks的第三方控件,对于<description>标签中的XML文档,可以直接使用以下代码进行染色:


        对于上图的情况,使用贪心算法将“分组1”和“分组3”的字体颜色进行提取,并构造XML数据,这里“分组1”和“分组3”字体颜色一样,贪心算法计算的结果是将整个分组封装成一个标签。

        之所以说这个算法复杂是因为实际的情况远比上面的例子要复杂,需要处理各种边界问题,下面将关键代码贴出,其复杂性大家一看便知:

// Copyright © 2012 123u. All Rights Reserved// 作者:凝霜  博客 http://blog.csdn.net/mdl13412using System;using System.Collections.Generic;using System.Linq;using System.Text;using RichTextBoxLinks;using System.Windows.Forms;using System.Xml;using System.Drawing;namespace MissionEditor{    class RichTextBoxExRtfBinder    {        #region // 通用        /// <summary>        /// 获取XML标签中的颜色        /// </summary>        /// <param name="color"></param>        /// <returns></returns>        public static Color GetRgb(String color)        {            // 解析失败则返回黑色            try            {                int pos = 0;                String r = "";                String g = "";                String b = "";                String a = "";                while ('0' <= color[pos] && color[pos] <= '9')                {                    r += color[pos];                    ++pos;                }                ++pos;                while ('0' <= color[pos] && color[pos] <= '9')                {                    g += color[pos];                    ++pos;                }                ++pos;                while ('0' <= color[pos] && color[pos] <= '9')                {                    b += color[pos];                    ++pos;                }                ++pos;                while (pos < color.Length && '0' <= color[pos] && color[pos] <= '9')                {                    a += color[pos];                    pos++;                }                Color c = Color.FromArgb(Convert.ToInt32(a), Convert.ToInt32(r), Convert.ToInt32(g), Convert.ToInt32(b));                return c;            }            catch (Exception)            {                return Color.Black;            }         }        /// <summary>        /// 获取RTF标签中的颜色        /// </summary>        /// <param name="rtf"></param>        /// <returns></returns>        private static Color GetRtfColor(String rtf)        {            // rtf字符串中的字体颜色相关字符串格式 {\colortbl ;\red255\green0\blue0;}            try            {                int colorPos = rtf.IndexOf("{\\colortbl");                if (-1 == colorPos)                    return Color.Black;                colorPos += "{\\colortbl".Length;                int pos = 0;                String r = "";                String g = "";                String b = "";                while (!('0' <= rtf[colorPos + pos] && rtf[colorPos + pos] <= '9'))                    ++pos;                while ('0' <= rtf[colorPos + pos] && rtf[colorPos + pos] <= '9')                {                    r += rtf[colorPos + pos];                    ++pos;                }                while (!('0' <= rtf[colorPos + pos] && rtf[colorPos + pos] <= '9'))                    ++pos;                while ('0' <= rtf[colorPos + pos] && rtf[colorPos + pos] <= '9')                {                    g += rtf[colorPos + pos];                    ++pos;                }                while (!('0' <= rtf[colorPos + pos] && rtf[colorPos + pos] <= '9'))                    ++pos;                while ('0' <= rtf[colorPos + pos] && rtf[colorPos + pos] <= '9')                {                    b += rtf[colorPos + pos];                    ++pos;                }                Color c = Color.FromArgb(Convert.ToInt32(r), Convert.ToInt32(g), Convert.ToInt32(b));                return c;            }            catch (Exception)            {                return Color.Black;            }        }        #endregion        #region // 供MainForm中的任务描述RichTextBoxEx使用        /// <summary>        /// 供MainForm中的任务描述RichTextBoxEx使用        /// 将任务描述字体染色并设置变量超链接        /// </summary>        /// <param name="missionId">要处理的任务Id</param>        /// <param name="editor">MainForm中的任务描述编辑框</param>        public static void ShowRtfDescription(String missionId, RichTextBoxEx editor)        {            QuestXmlHandler.QuestInfo qi = (QuestXmlHandler.QuestInfo)QuestXmlHandler.QuestInfos[missionId];            StringBuilder description = new StringBuilder();            // 以行为解析单元            String line = "";            editor.SelectAll();            editor.Text = "";            try            {                int curPos = qi.UserDescription.Description.IndexOf("\n");                int lastPos = 0;                while (-1 != curPos)                {                    line = qi.UserDescription.Description.Substring(lastPos, curPos - lastPos);                    ShowRtfDescriptionImpl(line, editor);                    lastPos = curPos + 1;                    curPos = qi.UserDescription.Description.IndexOf("\n", lastPos);                    editor.SelectedText = "\n";                }            }            catch (System.Exception ex)            {                Logger.LogFunctionError("RichEditBoxExRtfBinder.ShowRtfDescription()", ex.Message);            }        }        private static void ShowRtfDescriptionImpl(String line, RichTextBoxEx editor)        {            // 用户描述部分只有两种标签<n>和<a>            int nTagPos = line.IndexOf("<n");            int aTagPos = line.IndexOf("<a");            while (-1 != nTagPos || -1 != aTagPos)            {                try                {                    XmlDocument doc = new XmlDocument();                    if (-1 == nTagPos)  // 解析<a>                    {                        aTagPos = ParseRtfDescriptionATag(line, editor, aTagPos, doc);                    }                    else if (-1 == aTagPos) // 解析<n>                    {                        nTagPos = ParseRtfDescriptionNTag(line, editor, nTagPos, doc);                    }                    else                    {                        // 同时解析<a>和<n>                        if (nTagPos < aTagPos)                        {                            nTagPos = ParseRtfDescriptionNTag(line, editor, nTagPos, doc);                        }                        else if (nTagPos > aTagPos)                        {                            aTagPos = ParseRtfDescriptionATag(line, editor, aTagPos, doc);                        }                        else                        {                            throw new Exception("节点解析错误<n>和<a>位移相同");                        }                    }                }                catch (Exception ex)                {                    Logger.LogFunctionError("RichEditBoxExRtfBinder.ShowRtfDescriptionImpl()", ex.Message);                }            }        }        /// <summary>        /// 解析<a>节点        /// </summary>        /// <param name="line">行文本</param>        /// <param name="editor">绑定的编辑框</param>        /// <param name="aTagPos"><a>标签的位移</param>        /// <param name="doc">xml节点</param>        /// <returns>下一个<a>节点位移</returns>        private static int ParseRtfDescriptionATag(String line, RichTextBoxEx editor, int aTagPos, XmlDocument doc)        {            int endATagPos = line.IndexOf("</a>", aTagPos);            if (-1 == endATagPos)                throw new Exception("节点解析错误" + line);            // 使用XML来简化编程            String text = line.Substring(aTagPos, endATagPos - aTagPos + "</a>".Length);            XmlElement Root = doc.CreateElement("node");            Root.InnerXml = text;            doc.AppendChild(Root);            try            {                editor.InsertLink(doc.ChildNodes[0].ChildNodes[0].InnerText, doc.ChildNodes[0].InnerXml);            }            catch (System.Exception)            {            }            return line.IndexOf("<a", endATagPos + "</a>".Length);        }        /// <summary>        /// 解析<n>节点        /// </summary>        /// <param name="line">行文本</param>        /// <param name="editor">绑定的编辑框</param>        /// <param name="aTagPos"><n>标签的位移</param>        /// <param name="doc">xml节点</param>        /// <returns>下一个<n>节点位移</returns>        private static int ParseRtfDescriptionNTag(String line, RichTextBoxEx editor, int nTagPos, XmlDocument doc)        {            int endNTagPos = line.IndexOf("</n>", nTagPos);            if (-1 == endNTagPos)                throw new Exception("节点解析错误" + line);            String text = line.Substring(nTagPos, endNTagPos - nTagPos + "</n>".Length);            XmlElement Root = doc.CreateElement("node");            Root.InnerXml = text;            doc.AppendChild(Root);            try            {                // 如果获取颜色失败,则判断是否为任务变量                String color = doc.ChildNodes[0].ChildNodes[0].Attributes["color"].Value;                editor.SelectionColor = GetRgb(color);                editor.SelectedText = doc.ChildNodes[0].ChildNodes[0].InnerText;            }            catch (System.Exception)            {                try                {                    // 获取任务变量信息                    String var = doc.ChildNodes[0].ChildNodes[0].Attributes["var"].Value;                    String task = doc.ChildNodes[0].ChildNodes[0].Attributes["task"].Value;                    // <n var="变量名" task="任务">                    editor.InsertLink("任务变量", doc.ChildNodes[0].InnerXml);                }                catch (System.Exception)                {                    try                    {                        // 默认颜色的普通文本                        editor.SelectionColor = Color.Black;                        editor.SelectedText = doc.ChildNodes[0].ChildNodes[0].InnerText;                    }                    catch (System.Exception)                    {                    }                }            }            return line.IndexOf("<n", endNTagPos + "</n>".Length);        }        /// <summary>        /// 将编辑框中的RTF转换为XML文件        /// </summary>        /// <param name="editor">MainForm中的任务描述编辑框</param>        /// <returns>XML文本</returns>        public static String ConverterRtfDescriptionToXml(RichTextBoxEx editor)        {            StringBuilder description = new StringBuilder();            try            {                // 以行为解析单元                int lineStart = 0;                int lineEnd = editor.Text.IndexOf("\n");                // 只有一行数据且没有换行                if (-1 == lineEnd && 0 != editor.Text.Length)                    lineEnd = editor.Text.Length;                int length = lineEnd - lineStart;                String line = "";                while (-1 != lineEnd)                {                    length = lineEnd - lineStart;                    line = editor.Text.Substring(lineStart, lineEnd - lineStart);                    if ("" != line) // 跳过空白行                    {                        try                        {                            List<Tuple<int, int, String>> hrefs = ConverterRtfDescriptionHrefHelper(line);                            if (null == hrefs)                                throw new Exception("获取链接集合错误");                            ConverterRtfDescriptionTags(description, line, hrefs, editor, lineStart);                        }                        catch (System.Exception ex)                        {                            Logger.LogFunctionError("RichTextBoxExRtfBinder.ConverterRtfDescriptionToInnerXml()[Tag_解析标签]", ex.Message);                            return "";                        }                        description.Append("\n");                    }                    lineStart = lineEnd + 1;                    if (lineStart >= editor.Text.Length)                    {                        lineEnd = -1;                    }                    else                    {                        lineEnd = editor.Text.IndexOf("\n", lineStart);                        if (-1 == lineEnd)                            lineEnd = editor.Text.Length;                    }                }                return description.ToString();            }            catch (Exception ex)            {                Logger.LogFunctionError("RichTextBoxExRtfBinder.ConverterRtfDescriptionToInnerXml()[Tag_解析一行数据]", ex.Message);                return "";            }          }        /// <summary>        /// 解析出一行中的所有超链接<a>和任务变量<n var>        /// </summary>        /// <param name="line">行文本</param>        /// <returns>所有超链接的集合</returns>        private static List<Tuple<int, int, String>> ConverterRtfDescriptionHrefHelper(String line)        {            try            {                // 所有变量链接的起始点,终点,XML数据                List<Tuple<int, int, String>> hrefs = new List<Tuple<int, int, String>>();                // 首先找出所有的变量链接                int hrefStart = 0;                int hrefEnd = 0;                int hrefTag = line.IndexOf("#");                while (-1 != hrefTag)                {                    int pos1 = 0;                    int pos2 = 0;                    String href = "";                    String info = "";                    String var = "";                    switch (line[hrefTag + 2])                    {                    case 'n':                        // <n var="get_物品2" task="2">                        pos1 = line.IndexOf("</n>", hrefTag);                        var = line.Substring(hrefTag + "#".Length, pos1 + "</n>".Length - hrefTag - "#".Length);                        hrefStart = hrefTag - "任务变量".Length;                        hrefEnd = pos1 + "</n>".Length;                        hrefs.Add(new Tuple<int, int, String>(hrefStart, hrefEnd, var));                        break;                    case 'a':                        // 格式为  NPC(x,y)#<a href="goto x,y">NPC(x,y)</a>                        pos1 = line.IndexOf(">", hrefTag);                        pos2 = line.IndexOf("</a>", hrefTag);                        href = line.Substring(hrefTag + "#".Length, pos2 + "</a>".Length - hrefTag - "#".Length);                        info = line.Substring(pos1 + 1, pos2 - pos1 - ">".Length);                        hrefStart = hrefTag - info.Length;                        hrefEnd = pos2 + "</a>".Length;                        hrefs.Add(new Tuple<int, int, String>(hrefStart, hrefEnd, href));                        break;                    default:                        throw new Exception("意外的标签,请仔细检查任务文件");                    }                    hrefTag = line.IndexOf("#", hrefTag + "#".Length);                }                return hrefs;            }            catch (Exception ex)            {                Logger.LogFunctionError("RichTextBoxExRtfBinder.ConverterRtfDescriptionHref()[Tag_解析超链接]", ex.Message);                return null;            }        }        /// <summary>        /// 构造普通的<n>节点        /// </summary>        /// <param name="description">XML构建器</param>        /// <param name="color"><n>节点的颜色</param>        /// <param name="text"><n>节点的内容</param>        private static void ConverterRtfDescriptionMakeNTag(StringBuilder description, Color color, String text)        {            if (Color.Black.R == color.R && Color.Black.G == color.G && Color.Black.B == color.B)                description.AppendFormat("<n>{0}</n>", text);            else                description.AppendFormat("<n color=\"{0},{1},{2},0\">{3}</n>", Convert.ToInt32(color.R), Convert.ToInt32(color.G), Convert.ToInt32(color.B), text);        }        /// <summary>        ///  贪心算法实现标签的解析        ///  将连续相同颜色的标签分为一组,并构建XML        /// </summary>        /// <param name="description"></param>        /// <param name="wordBlock"></param>        /// <param name="editor"></param>        /// <param name="blockStart"></param>        private static void ConverterRtfDescriptionProcessWordBlock(StringBuilder description, String wordBlock, RichTextBoxEx editor, int blockStart)        {            Color lastWordColor = Color.Black;            Color curWordColor = Color.Black;            editor.Select(blockStart, 1);            curWordColor = GetRtfColor(editor.SelectedRtf);            lastWordColor = curWordColor;            // 本行只有一个字符的情况            if (1 == wordBlock.Length)            {                ConverterRtfDescriptionMakeNTag(description, curWordColor, editor.SelectedText);                return;            }            int counter = 1;            for (int i = 1; i < wordBlock.Length; ++i)            {                editor.Select(blockStart + i, 1);                curWordColor = GetRtfColor(editor.SelectedRtf);                if (wordBlock.Length - 1 == i)                {                    if (lastWordColor == curWordColor)                    {                        editor.Select(blockStart + i - counter, counter + 1);                        ConverterRtfDescriptionMakeNTag(description, curWordColor, editor.SelectedText);                    }                    else                    {                        editor.Select(blockStart + i - counter, counter);                        ConverterRtfDescriptionMakeNTag(description, lastWordColor, editor.SelectedText);                        editor.Select(blockStart + i, 1);                        ConverterRtfDescriptionMakeNTag(description, curWordColor, editor.SelectedText);                    }                }                else                {                    if (lastWordColor == curWordColor)                    {                        ++counter;                    }                    else                    {                        editor.Select(blockStart + i - counter, counter);                        ConverterRtfDescriptionMakeNTag(description, lastWordColor, editor.SelectedText);                        counter = 1;                    }                }                lastWordColor = curWordColor;            }        }        /// <summary>        /// 转换RTF到XML        /// </summary>        /// <param name="description"></param>        /// <param name="line"></param>        /// <param name="hrefs"></param>        /// <param name="editor"></param>        /// <param name="lineStart"></param>        private static void ConverterRtfDescriptionTags(StringBuilder description, String line, List<Tuple<int, int, String>> hrefs, RichTextBoxEx editor, int lineStart)        {            try            {                if (0 == hrefs.Count)   // 处理没有超链接的行                    ConverterRtfDescriptionProcessWordBlock(description, line, editor, lineStart);                else                {                    #region // 处理有超链接的行                    // 非链接字符串区块                    int blockStart = 0;                    int blockEnd = hrefs[0].Item1;                    String wordBlock = line.Substring(blockStart, blockEnd - blockStart);                    if (blockStart == blockEnd)                    {                        #region // 一行的起始是链接的情况                        description.Append(hrefs[0].Item3);                        for (int i = 1; i < hrefs.Count; ++i)                        {                            blockStart = hrefs[i - 1].Item2;                            blockEnd = hrefs[i].Item1;                            wordBlock = line.Substring(blockStart, blockEnd - blockStart);                            ConverterRtfDescriptionProcessWordBlock(description, wordBlock, editor, lineStart + blockStart);                            description.Append(hrefs[i].Item3);                            if (0 == wordBlock.Length)                                continue;                        }                        // 判断最后一个链接后是否还有普通文本                        if (hrefs[hrefs.Count - 1].Item2 != line.Length)                        {                            wordBlock = line.Substring(hrefs[hrefs.Count - 1].Item2, line.Length - hrefs[hrefs.Count - 1].Item2);                            ConverterRtfDescriptionProcessWordBlock(description, wordBlock, editor, lineStart + hrefs[hrefs.Count - 1].Item2);                        }                        #endregion                    }                    else                    {                        #region // 一行起始是普通文本的情况                        ConverterRtfDescriptionProcessWordBlock(description, wordBlock, editor, lineStart + blockStart);                        if (1 == hrefs.Count)                            description.Append(hrefs[0].Item3);                        else                        {                            for (int i = 1; i < hrefs.Count; ++i)                            {                                description.Append(hrefs[i].Item3);                                blockStart = hrefs[i - 1].Item2;                                blockEnd = hrefs[i].Item1;                                wordBlock = line.Substring(blockStart, blockEnd - blockStart);                                ConverterRtfDescriptionProcessWordBlock(description, wordBlock, editor, lineStart + blockStart);                                 if (0 == wordBlock.Length)                                    continue;                            }                            description.Append(hrefs[hrefs.Count - 1].Item3);                        }                        // 判断最后一个链接后是否还有普通文本                        if (hrefs[hrefs.Count - 1].Item2 != line.Length)                        {                            wordBlock = line.Substring(hrefs[hrefs.Count - 1].Item2, line.Length - hrefs[hrefs.Count - 1].Item2);                            ConverterRtfDescriptionProcessWordBlock(description, wordBlock, editor, lineStart + hrefs[hrefs.Count - 1].Item2);                        }                        #endregion                    }                    #endregion                }            }            catch (System.Exception ex)            {                Logger.LogFunctionError("RichTextBoxExRtfBinder.ConverterRtfDescriptionTags()", ex.Message);            }        }        #endregion        #region // 供MainForm中的消息RichTextBoxEx使用        public static void ShowRtfMessage(String missionId, RichTextBoxEx editor)        {            QuestXmlHandler.QuestInfo qi = (QuestXmlHandler.QuestInfo)QuestXmlHandler.QuestInfos[missionId];            StringBuilder messageBuilder = new StringBuilder();            String line = "";            editor.SelectAll();            editor.Text = "";            if (qi.UserDescription.HasMessages())            {                for (int i = 0; i < qi.UserDescription.Messages.Count; ++i)                    messageBuilder.Append(qi.UserDescription.Messages[i] + "\n");            }            else            {                return;            }            String messages = messageBuilder.ToString();            try            {                int curPos = messages.IndexOf("\n");                int lastPos = 0;                while (-1 != curPos)                {                    line = messages.Substring(lastPos, curPos - lastPos);                    ShowRtfMessageImpl(line, editor);                    lastPos = curPos + 1;                    curPos = messages.IndexOf("\n", lastPos);                    editor.SelectedText = "\n";                }            }            catch (System.Exception ex)            {                Logger.LogFunctionError("RichEditBoxExRtfBinder.ShowRtfMessage()", ex.Message);            }        }        private static void ShowRtfMessageImpl(String line, RichTextBoxEx editor)        {            // 用户消息部分只有一种标签<n>            int nTagPos = line.IndexOf("<n");            while (-1 != nTagPos)            {                try                {                    XmlDocument doc = new XmlDocument();                    nTagPos = ParseRtfMessageNTag(line, editor, nTagPos, doc);                }                catch (Exception ex)                {                    Logger.LogFunctionError("RichEditBoxExRtfBinder.ShowRtfDescriptionImpl()", ex.Message);                }            }        }        private static int ParseRtfMessageNTag(String line, RichTextBoxEx editor, int nTagPos, XmlDocument doc)        {            int endNTagPos = line.IndexOf("</n>", nTagPos);            if (-1 == endNTagPos)                throw new Exception("节点解析错误" + line);            String text = line.Substring(nTagPos, endNTagPos - nTagPos + "</n>".Length);            XmlElement Root = doc.CreateElement("node");            Root.InnerXml = text;            doc.AppendChild(Root);            try            {                String color = doc.ChildNodes[0].ChildNodes[0].Attributes["color"].Value;                editor.SelectionColor = GetRgb(color);                editor.SelectedText = doc.ChildNodes[0].ChildNodes[0].InnerText;            }            catch (System.Exception)            {                try                {                    String var = doc.ChildNodes[0].ChildNodes[0].Attributes["var"].Value;                    String task = doc.ChildNodes[0].ChildNodes[0].Attributes["task"].Value;                    // <n var="变量名" task="任务">                    editor.InsertLink("任务变量", doc.ChildNodes[0].InnerXml);                }                catch (System.Exception)                {                    try                    {                        editor.SelectionColor = Color.Black;                        editor.SelectedText = doc.ChildNodes[0].ChildNodes[0].InnerText;                    }                    catch (System.Exception)                    {                    }                }            }            return line.IndexOf("<n", endNTagPos + "</n>".Length);        }        public static String ConverterRtfMessageToXml(RichTextBoxEx editor)        {            StringBuilder message = new StringBuilder();            try            {                // 以行为解析单元                int lineStart = 0;                int lineEnd = editor.Text.IndexOf("\n");                // 只有一行数据且没有换行                if (-1 == lineEnd && 0 != editor.Text.Length)                    lineEnd = editor.Text.Length;                int length = lineEnd - lineStart;                String line = "";                while (-1 != lineEnd)                {                    length = lineEnd - lineStart;                    line = editor.Text.Substring(lineStart, lineEnd - lineStart);                    if ("" != line) // 跳过空白行                    {                        try                        {                            List<Tuple<int, int, String>> hrefs = ConverterRtfMessageHrefHelper(line);                            if (null == hrefs)                                throw new Exception("获取链接集合错误");                            ConverterRtfMessageTags(message, line, hrefs, editor, lineStart);                        }                        catch (System.Exception ex)                        {                            Logger.LogFunctionError("RichTextBoxExRtfBinder.ConverterRtfMessageToInnerXml()[Tag_解析标签]", ex.Message);                            return "";                        }                        message.Append("\n");                    }                    lineStart = lineEnd + 1;                    if (lineStart >= editor.Text.Length)                    {                        lineEnd = -1;                    }                    else                    {                        lineEnd = editor.Text.IndexOf("\n", lineStart);                        if (-1 == lineEnd)                            lineEnd = editor.Text.Length;                    }                }                return message.ToString();            }            catch (Exception ex)            {                Logger.LogFunctionError("RichTextBoxExRtfBinder.ConverterRtfMessageToInnerXml()[Tag_解析一行数据]", ex.Message);                return "";            }          }        private static List<Tuple<int, int, String>> ConverterRtfMessageHrefHelper(String line)        {            try            {                // 所有变量链接的起始点,终点,XML数据                List<Tuple<int, int, String>> hrefs = new List<Tuple<int, int, String>>();                // 首先找出所有的变量链接                int hrefStart = 0;                int hrefEnd = 0;                int hrefTag = line.IndexOf("#");                while (-1 != hrefTag)                {                    int pos = 0;                    String var = "";                    switch (line[hrefTag + 2])                    {                    case 'n':                        // <n var="get_物品2" task="2">                        pos = line.IndexOf("</n>", hrefTag);                        var = line.Substring(hrefTag + "#".Length, pos + "</n>".Length - hrefTag - "#".Length);                        hrefStart = hrefTag - "任务变量".Length;                        hrefEnd = pos + "</n>".Length;                        hrefs.Add(new Tuple<int, int, String>(hrefStart, hrefEnd, var));                        break;                    default:                        throw new Exception("意外的标签,请仔细检查任务文件");                    }                    hrefTag = line.IndexOf("#", hrefTag + "#".Length);                }                return hrefs;            }            catch (Exception ex)            {                Logger.LogFunctionError("RichTextBoxExRtfBinder.ConverterRtfMessageHrefHelper()[Tag_解析超链接]", ex.Message);                return null;            }        }        private static void ConverterRtfMessageMakeNTag(StringBuilder message, Color color, String text)        {            if (Color.Black.R == color.R && Color.Black.G == color.G && Color.Black.B == color.B)                message.AppendFormat("<n>{0}</n>", text);            else                message.AppendFormat("<n color=\"{0},{1},{2},0\">{3}</n>", Convert.ToInt32(color.R), Convert.ToInt32(color.G), Convert.ToInt32(color.B), text);        }        private static void ConverterRtfMessageProcessWordBlock(StringBuilder message, String wordBlock, RichTextBoxEx editor, int blockStart)        {            Color lastWordColor = Color.Black;            Color curWordColor = Color.Black;            editor.Select(blockStart, 1);            curWordColor = GetRtfColor(editor.SelectedRtf);            lastWordColor = curWordColor;            // 本行只有一个字符的情况            if (1 == wordBlock.Length)            {                ConverterRtfMessageMakeNTag(message, curWordColor, editor.SelectedText);                return;            }            int counter = 1;            for (int i = 1; i < wordBlock.Length; ++i)            {                editor.Select(blockStart + i, 1);                curWordColor = GetRtfColor(editor.SelectedRtf);                if (wordBlock.Length - 1 == i)                {                    if (lastWordColor == curWordColor)                    {                        editor.Select(blockStart + i - counter, counter + 1);                        ConverterRtfMessageMakeNTag(message, curWordColor, editor.SelectedText);                    }                    else                    {                        editor.Select(blockStart + i - counter, counter);                        ConverterRtfMessageMakeNTag(message, lastWordColor, editor.SelectedText);                        editor.Select(blockStart + i, 1);                        ConverterRtfMessageMakeNTag(message, curWordColor, editor.SelectedText);                    }                }                else                {                    if (lastWordColor == curWordColor)                    {                        ++counter;                    }                    else                    {                        editor.Select(blockStart + i - counter, counter);                        ConverterRtfMessageMakeNTag(message, lastWordColor, editor.SelectedText);                        counter = 1;                    }                }                lastWordColor = curWordColor;            }        }        private static void ConverterRtfMessageTags(StringBuilder message, String line, List<Tuple<int, int, String>> hrefs, RichTextBoxEx editor, int lineStart)        {            try            {                if (0 == hrefs.Count)   // 处理没有超链接的行                    ConverterRtfMessageProcessWordBlock(message, line, editor, lineStart);                else                {                    #region // 处理有超链接的行                    // 非链接字符串区块                    int blockStart = 0;                    int blockEnd = hrefs[0].Item1;                    String wordBlock = line.Substring(blockStart, blockEnd - blockStart);                                        if (blockStart == blockEnd)                    {                        #region // 一行的起始是链接的情况                        message.Append(hrefs[0].Item3);                        for (int i = 1; i < hrefs.Count; ++i)                        {                            blockStart = hrefs[i - 1].Item2;                            blockEnd = hrefs[i].Item1;                            wordBlock = line.Substring(blockStart, blockEnd - blockStart);                            ConverterRtfMessageProcessWordBlock(message, wordBlock, editor, lineStart + blockStart);                            message.Append(hrefs[i].Item3);                            if (0 == wordBlock.Length)                                continue;                        }                        // 判断最后一个链接后是否还有普通文本                        if (hrefs[hrefs.Count - 1].Item2 != line.Length)                        {                            wordBlock = line.Substring(hrefs[hrefs.Count - 1].Item2, line.Length - hrefs[hrefs.Count - 1].Item2);                            ConverterRtfMessageProcessWordBlock(message, wordBlock, editor, lineStart + hrefs[hrefs.Count - 1].Item2);                        }                        #endregion                    }                    else                    {                        #region // 一行起始是普通文本的情况                        ConverterRtfMessageProcessWordBlock(message, wordBlock, editor, lineStart + blockStart);                        if (1 == hrefs.Count)                            message.Append(hrefs[0].Item3);                        else                        {                            for (int i = 1; i < hrefs.Count; ++i)                            {                                message.Append(hrefs[i].Item3);                                blockStart = hrefs[i - 1].Item2;                                blockEnd = hrefs[i].Item1;                                wordBlock = line.Substring(blockStart, blockEnd - blockStart);                                ConverterRtfMessageProcessWordBlock(message, wordBlock, editor, lineStart + blockStart);                                if (0 == wordBlock.Length)                                    continue;                            }                            message.Append(hrefs[hrefs.Count - 1].Item3);                        }                        // 判断最后一个链接后是否还有普通文本                        if (hrefs[hrefs.Count - 1].Item2 != line.Length)                        {                            wordBlock = line.Substring(hrefs[hrefs.Count - 1].Item2, line.Length - hrefs[hrefs.Count - 1].Item2);                            ConverterRtfMessageProcessWordBlock(message, wordBlock, editor, lineStart + hrefs[hrefs.Count - 1].Item2);                        }                        #endregion                    }                    #endregion                }            }            catch (System.Exception ex)            {                Logger.LogFunctionError("RichTextBoxExRtfBinder.ConverterRtfMessageTags()", ex.Message);            }        }        #endregion    }}

总结        衡量任务编辑器的好坏没有一个固定的标准,只要符合项目的实际需要即可,不过可以肯定的是,“所见即所得”的功能越强大、操作越简单,那么这个编辑器的价值就越大。欢迎大家探讨游戏编辑器的相关话题,包括但不局限于任务编辑器:-)

热点排行