关于自己编写简单游戏编辑器的介绍

关于自己编写简单游戏编辑器的介绍

该编辑器编写的初衷是为了帮助游戏一些功能的开发比如新手引导,成就等等。现在编写的是一个框架,具体功能需要自行扩展。

 

目录

一、编辑器结构(原始数据+触发器)

 1、编辑器总体结构

2、原始数据

3、触发器数据

4、枚举数据

5、编辑数据存储

二、运行时结构

三、一个完整的实例

 

一、编辑器结构(原始数据+触发器)  1、编辑器总体结构

编辑器有四大数据模块组成它们分别是原始数据、触发器数据、枚举数据结构、数据存储数据结构。下面会逐一介绍每个数据,关于它们的数据构成如何扩展等。

原始数据是最基本的数据,它是组成整个编辑器数据的基石,标识出该数据是做什么的。

触发器数据在编辑器和运行时都是需要用的数据,在编辑器中它用来存储编辑好的数据,在运行时中它用来处理游戏传出的数据。可以看出触发器数据在整个编辑器和运行时中有着重要的地位。

枚举数据结构是用于辅助类的数据,该数据用于选择器数据。

数据存储 分为编辑数据存储和导出数据存储,编辑数据用于保存编辑器数据,导出数据用于运行时数据的运用。

2、原始数据

原始数据有一个总类OrigDataParent。

OrigDataParent是一个抽象类,包含四个字段,变量name表示数据的名称,它将在选择[事件/环境/动作]中显现该名字。变量id表示该段数据的唯一id,它对应着不同的枚举值,[事件/环境/动作]对应的不同的枚举,这个id代表该唯一的原始数据,在触发器中的origId就是对应着该id,[事件/环境/动作]三个不同的枚举在运行时中有着唯一区别的作用。变量descrip表示数据描述,用来给使用者一个具体的解释。变量type表示所属类型它用于为原始数据分类型,对应着三个不同的枚举分别是OrigEventTypeEnum/OrigConditionTypeEnum/OrigActionTypeEnum。

OrigDataParent继承了接口IOrigData,该接口包含了两个方法Show()和Save (params object[] obj),其中Show用来显示原始数据中的内容供使用者填写/选择数据,后面将会举例说明。Save用来保存使用者编辑的数据。

另外比较重要的一点是为了能够读取数据的方便开发者扩展新的触发器时需要给出指定的命名空间DataStruct。

下面是元数据OrigDataParent与接口IOrigData的代码。

public interface IOrigData { /// /// 显示Panel数据 /// /// List Show(); /// /// 保存数据 /// /// TriggerDataParent Save (params object[] obj) ; }namespace DataStruct{ [Instantiation] public abstract class OrigDataParent : IOrigData { public string name;//数据名称 /// /// 唯一id /// 对应ThreeOrigEnumType中三个枚举中的值 /// public int id; /// /// 数据描述 /// public string descrip; /// /// //所属类型,默认全部 /// 对应枚举 /// OrigEventTypeEnum/OrigConditionTypeEnum/OrigActionTypeEnum/OrigOtherTypeEnum /// 的值 /// public int type = 0; public OrigDataParent(string name, int id) { this.name = name; this.id = id; } / / 保存数据的方法 / / obj[0]=EditorTriggerDataStruct //public abstract void Save(params object[] obj); public abstract TriggerDataParent Save(params object[] obj); /// /// 显示内容的方法 /// /// public abstract List Show(); public virtual string Descrip() { return name + "——" + descrip; } public override string ToString() { return name; } }}

 

原始数据下面分三个类型,事件、环境、动作,这三个类型都有一个父类它们分别是OrigEventDataParent、OrigConditionDataParent、OrigActionDataParent它们都继承父类OrigDataParent。这三个类也均是抽象类,具体内容可见项目代码。

 

3、触发器数据

触发器数据有一个总类TriggerDataParent,它是一个抽象类。

TriggerDataParent中有两个字段,变量origId它对应于原始数据唯一的id,标识该触发器要用于做什么。变量fullName表示该触发器的完全限定名,该字段不需要开发者维护,该字段仅用于数据的存储与读取。另外比较重要的一点是为了能够读取数据的方便开发者扩展新的触发器时需要给出指定的命名空间DataStruct。下面是该抽象类的具体内容。

public interface IListVoluation { /// /// 传入一个List 类型 /// 将其转化为list具体类型 /// /// void listVoluation(List list); }namespace DataStruct{ /// /// 触发器数据总类 /// [TriggerTypeAttri] public abstract class TriggerDataParent : IListVoluation { /// /// 对应原始数据的唯一id /// public float origId; public string fullName; public TriggerDataParent(float origId) { this.origId = origId; fullName = GetType().FullName; } public TriggerDataParent() { fullName = GetType().FullName; } public void SetOrigId(int origId) { this.origId = origId; } /// /// 创建额外树节点, /// 这个节点将作为本身节点的子节点 /// 可以参考TriggerMultipleConditionData的重写方法--creatSelfTreeNode /// 如无特殊需要不必重写 /// /// public virtual TreeNode creatSelfTreeNode() { return null; } /// /// 对触发器的额外描述 /// 如无特殊需要不必重写 /// /// public virtual string Descript() { return null; } /// /// 如果子类中有List成员变量,请务必实现该方法 /// 传入一个List 类型,将其转化为list具体类型 /// /// 传入的list是对应于所需要的list,用于数据读取 public virtual void listVoluation(List list) { } }}

触发器结构下面分三个类型,事件、环境、动作,这三个类型都有一个父类它们分别是TriggerDataParent、TriggerConditionDataParent、TriggerActionDataParent它们都继承父类TriggerDataParent。这三个类也均是抽象类,具体内容可见项目代码。

4、枚举数据

枚举数据是自定义的一个类EnumClassParent,它用来作为选择器数据,所有的选择器数据都要继承该父类,这个类不参与存储,在编辑器开始运行时将会运用反射自动加载所有继承该类的子类。该类有四个字段,它们分别是:listEnum它是一个list列表类型是EnumItem,用来缓存枚举单位;name它表示选择器的名字;enumType枚举类型值,对应于EnumType枚举中的值;字典dicEnums。下面给出该代码:

public class EnumItem { public string name; public int value; public EnumItem(string name, int value) { this.name = name; this.value = value; } } [Instantiation] public abstract class EnumClassParent { public List listEnum = new List(); public string name; /// /// 对应于EnumType的值 /// public int enumType; /// /// /key表示EnumItem中的value值 /// [JsonIgnore] public Dictionary dicEnums = new Dictionary(); public EnumClassParent(string name, int enumType) { this.name = name; this.enumType = enumType; } protected void creat(string name, int value = -1) { if (value == -1) { value = listEnum.Count; } EnumItem item = new EnumItem(name, value); listEnum.Add(item); if (dicEnums.ContainsKey(item.value)) { dicEnums[item.value] = item; } else { dicEnums.Add(item.value, item); } } public void setData() { if (listEnum.Count > 0) { foreach (var item in listEnum) { if (dicEnums.ContainsKey(item.value)) { dicEnums[item.value] = item; } else { dicEnums.Add(item.value, item); } } } } }/// /// 枚举类型标识 /// public enum EnumType { /// /// 对比计算器 /// ContrastCalculator, /// /// ui的类型 /// UIType, /// /// 触发器枚举 /// Trigger, /// /// 创建某个物体的枚举 /// CreatItem, /// /// 单位类型 /// ItemType, /// /// 玩家GM命令 /// PlayerGM, }

下面给出一个具体的选择器的代码:

namespace DataStruct{ /// /// 对比计算器 /// public class EnumContrastCalculator : EnumClassParent { public EnumContrastCalculator() : base("对比计算器", (int)EnumType.ContrastCalculator) { creat("等于",1000); creat("不等于",1001); creat("小于等于",1002); creat("大于等于",1003); creat("小于",1004); creat("大于",1005); } }}

改代码在编辑器运行是对应的数据显示:

5、编辑数据存储

编辑器数据的存储结构由三个类构成,EditorTriggerDataStruct表示一个完整的触发器,其中包括该触发器内所有的事件环境动作数据,EditorData表示整个类的数据结构,EditorDataSave存储所有的类的数据同时也是保存的json数据。下面的一张图将会解释这三个类的关系。

该数据不需要开发者再次管理,整套管理流程已经书写完毕。

 

二、运行时结构

对于不同的游戏运行时结构会有所不同,现在给出一个特定的运行时结构。该运行时是在服务器使用。

1、编辑器导出数据的结构

ExportTriggerDataStruct类作为一个触发的结构它包括事件/环境/条件着三类数据,变量triggerStructIndex表示该触发器在该工程中的唯一序列,用于区别于其他的导出触发器数据结构。ExportTriggerDataStruct中有三个方法它们用于处理事件/环境/动作这些数据。下面给出代码:

@DataStructAnnotat@Scope("prototype")@Servicepublic class ExportTriggerDataStruct implements IListVoluation{public ArrayList list_event = new ArrayList();public ArrayList list_condition = new ArrayList();public ArrayList list_action = new ArrayList();public String fullName;public float triggerStructIndex;@Overridepublic void listVoluation(ArrayList list){if (list == null || list.size() 0){for (TriggerConditionDataParent con : list_condition){boolean res = con.DataProcessing(userId);if (!res){Tool.print_debug_level0("条件不满足不执行动作。对应的原始数据id=" + con.origId);return false;}}}return actionDataProcessing(userId);}/** * 执行动作 * @param userId * @return */public boolean actionDataProcessing(int userId){if (list_action != null && list_action.size() > 0){for (TriggerActionDataParent data : list_action){boolean res = data.DataProcessing(userId);Tool.print_debug_level0("执行动作后的反馈。res=" + res + ",ActionData origId=" + data.origId);}}return true;}}

ExportData类中包含整个工程中所有的ExportTriggerDataStruct,我们从json数据中读取的最后会得到一个ExportData类,ExportData类继承了一个IListVoluation接口。listVoluation方法的作用见IListVoluation接口中的方法。代码如下:

@DataStructAnnotat @Scope("prototype")@Servicepublic class ExportData implements IListVoluation{public ArrayList listChuFaQi;public String fullName;@Overridepublic void listVoluation(ArrayList list){if (list == null || list.size() 0) {for (ExportTriggerDataStruct export : data.listChuFaQi) {map_allTriggerStructs.put(export.triggerStructIndex, export);addAllEventData(export.list_event, export.triggerStructIndex);}}}}private void addAllEventData(ArrayList list, float triggerStructIndex) {if (list == null || list.size() 0) {for (TriggerEventDataParent data : map_allEventDtas.get(origId)) {boolean res = data.DataProcessing(origId, objs);ExportTriggerDataStruct trigger_st = map_allTriggerStructs.get(data.triggerStructIndex);boolean returnRes = trigger_st.receiveEventResult(userId, data.triggerIndex, res);Tool.print_debug_level0("事件发生之后处理数据的结果=" + returnRes);return returnRes;}} else {return false;}}return false;}} 三、一个完整的实例

1、玩家GM命令为玩家添加装备

描述:在聊天的输入框输入指定的字符串获取装备。

a、原始数据结构

原始数据事件,代码如下:

//命名空间固定为DataStructnamespace DataStruct{ /// /// 原始事件数据需继承原始数据事件类OrigEventDataParent /// public class OrigEventPlayerGMStr : OrigEventDataParent { private string GM_str;//玩家GM命令起始字符串 public OrigEventPlayerGMStr() : base("玩家GM命令事件(指定起始字符串)", (int)OrignalEventDataEnum.playerGM_str) { descrip = "通过聊天窗口添加命令,比如(添加装备):add "; type = (int)OrigEventTypeEnum.玩家; } /// /// 保存触发器数据 /// /// /// public override TriggerDataParent Save(params object[] obj) { TriggerEventPlayerGMStr data = DataHandleManage.editorHandle.CreatNewTriggerDataClass(id); data.SetData(GM_str); return data; } /// /// 再panel中显示的数据,下面的图片中将会给出这个方法的用处 /// /// public override List Show() { List list = new List(); Label label = DataHandleManage.orignalHandle.creatLabel(name + "请输入GM命令起始字符(请尽量不要与其它已用字符重复):"); list.Add(label); TextBox textBox2 = DataHandleManage.orignalHandle.creatTextBox(); textBox2.TextChanged += (object sender, EventArgs e) => { GM_str = textBox2.Text; }; list.Add(textBox2); return list; } }}

运行图片如下:

注意上述图片中的内容是如何显示的按钮是如何调用save方法的都不需要开发者去处理,内部已经处理好了。在此玩家GM命令中不需要环境。

原始数据动作,代码如下:

//指定命名空间DataStructnamespace DataStruct{//原始数据动作继承动作的原始数据父类,此类已经在前面介绍过OrigActionDataParent public class OrigActionPlayerChatCommand : OrigActionDataParent { private int player_GM;//玩家GM命令字符串 private Button currentButton;//当前选中的button public OrigActionPlayerChatCommand() : base("玩家聊天命令", (int)OrignalActionDataEnum.playerChatCommand) { descrip = "给玩家添加装备。"; type = (int)OrigActionTypeEnum.玩家; } /// /// 保存触发器数据 /// /// /// public override TriggerDataParent Save(params object[] obj) { TriggerActionPlayerChatCommand data = DataHandleManage.editorHandle.CreatNewTriggerDataClass(id); data.SetData(player_GM); return data; } /// /// 再panel中显示的数据,下面的图片中将会给出这个方法的用处 /// /// public override List Show() { List list = new List();//这里已经提供创建Label的方法,使用Label时使用该方法 Label label = DataHandleManage.orignalHandle.creatLabel(name + ",点击可选择一个聊天命令类型。"); list.Add(label);//这里已经提供创建Button的方法,使用Button时使用该方法 Button button = DataHandleManage.orignalHandle.creatButton("点击选择"); button.MouseClick += ButtonClick; list.Add(button); return list; }//点击Button的事件 private void ButtonClick(object sender, MouseEventArgs e) { currentButton = (Button)sender; FuncDelegate func = new FuncDelegate(selectUIType); DataHandleManage.openTypeSelectForm(AllDataManage.allEnumClass.dicEnumParent[(int)EnumType.PlayerGM], func); } private void selectUIType(params object[] uiType) { EnumItem item = (EnumItem)uiType[0]; this.player_GM = item.value; currentButton.Text = item.name + "--点击选择"; } }}

运行图片如下:

b、触发器代码结构

触发器数据事件,代码如下:

//使用指定命名空间DataStructnamespace DataStruct{//该事件触发器需继承触发器事件类 TriggerEventDataParent public class TriggerEventPlayerGMStr : TriggerEventDataParent { public string GM_str; public void SetData(string GM_str) { this.GM_str = GM_str; }//描述该触发器内容,若有该触发器自己的自定义的变量则需要重写该方法用于展示该条触发器信息 public override string Descript() { string des = ""; OrigEventDataParent orig = AllDataManage.origData.dicEventData[(int)origId]; //string name = AllDataManage.allEnumClass.dicEnumParent[(int)EnumType.PlayerGM].dicEnums[(int)player_GM].name; des = orig.name + "(自定义的起始命令:" + this.GM_str + ")"; return des; } }}

触发器数据动作,代码如下:

//使用指定的命名空间DataStructnamespace DataStruct{//该动作触发器需继承触发器动作类TriggerActionDataParent public class TriggerActionPlayerChatCommand : TriggerActionDataParent { public float player_GM; public void SetData(int player_GM) { this.player_GM = player_GM; }//描述该触发器内容,若有该触发器自己的自定义的变量则需要重写该方法用于展示该条触发器信息 public override string Descript() { string des = ""; OrigActionDataParent orig = AllDataManage.origData.dicActionData[(int)origId]; string name = AllDataManage.allEnumClass.dicEnumParent[(int)EnumType.PlayerGM].dicEnums[(int)player_GM].name; ; des = orig.name + "(" + name + ")"; return des; } }}

运行图片如下:

 

 

 


比丘资源网 » 关于自己编写简单游戏编辑器的介绍

发表回复

提供最优质的资源集合

立即查看 了解详情