北大侠客行MUD论坛

 找回密码
 注册
搜索
热搜: 新手 wiki 升级
查看: 333|回复: 21

杰哥瞎扯蛋之我的地图数据结构

[复制链接]
发表于 2025-5-20 14:44:15 | 显示全部楼层 |阅读模式
俗话说的好,要想富,先修路。
要想玩MUD,特别是地图大的MUD,早早的开始积累地图数据是很重要的一点。

正好前一阵折腾Avaloniaui,做了一个地图数据管理器,正好整理了一下我的地图数据的格式。

发出来抛砖引玉一下。

注意,我分享的是 地图数据 的数据结构, 不是地图数据,也不是 怎么抓地图/定位/走迷宫,分享后连个属于折腾WIZ和玩家。
北大侠客行Mud(pkuxkx.net),最好的中文Mud游戏!
 楼主| 发表于 2025-5-20 14:50:52 | 显示全部楼层
首先,我们要明确一点,在北侠的环境下,各种地图信息格式百花齐放。

有文本流的,有脚本流的,有数据库流的,还有脚本专有格式流的。

对于我而言,一直是文本流的拥护者。

第一,便于维护/批量维护

第二,便于进行版本管理,通过对比能看看两个版本之间变动了那些信息。设置我还准备 做过diff的功能。

第三,便于编码处理。很多专有格式要搞个编码处理很蛋疼。

我一开始的预案是JSON/XML/类CSV选择一个。

但考虑到要做1比1 js/lua实现

XML第一个被排除了。

JSON的话,不方便按行做版本处理,而且做不同编码很麻烦,我也不高兴做。

所以我自己做了个带转义最多支持3层(实际写着写着到了5层)的类CSV格式。

人类能看,难写,方便脚本处理。

格式大概如下(非北侠数据):

  1. HMM1.0>UTF8
  2. Info>hell地图测试|1746810633|测试用数据
  3. Room>0|中央广场||||e,59,,1;enter dong,1927,,1;n,22,,1;s,40,,1;w,1,,1|
  4. Room>1|西大街||||e,0,,1;n,2,,1;s,5,,1;w,7,,1|
  5. Room>10|财主大院||||n,11,,1;s,9,,1|
  6. Room>100|石阶||||eu。,101,,1;wd,99,,1|
  7. Room>1000|民宅||||s,999,,1|
  8. Room>1001|黄土路||||n,1002,,1;se,999,,1|
  9. Room>1002|黄土路||||ne,1003,,1;s,1001,,1|
  10. Room>1003|渭汾流域||||n,1004,,1;ne,1712,,1;s,1025,,1;sw,1002,,1|
  11. Room>1004|黄河||||s,1003,,1;w,1005,,1|
  12. Room>1005|河套||||e,1004,,1;sw,1007,,1;w,1006,,1|
  13. Room>1006|青城||||e,1005,,1;n!,2048,,1|
  14. Room>1007|黄土高原||||ne,1005,,1;sw,1008,,1|
复制代码
第一行是文件信息,包括版本和编码(对,就是为了gbk和uft之争)
北大侠客行Mud(pkuxkx.net),最好的中文Mud游戏!
 楼主| 发表于 2025-5-20 15:08:42 | 显示全部楼层
核心概念。

对于地图来说,我们的用途主要是路径规划,房间查询,信息查询。

核心概念主要有4个

Room/Key房间/主键

每个房间,是一个抽象的概念,房间名描述之类属于附属信息,甚至是快照信息。只有为一的主键才是核心。

Key必须唯一,应该有实际意义,但不该有业务信息。

比如,扬州->广场,可以叫yzgc,但不该叫yz或者yz-(xxx任务信息)

因为业务可能会变,实际意义不随着业务逻辑变,要变也是信息重新录入。

这样,写代码是只要关心有一个key,不要关心其他的,其他的不属于核心功能。

Exit/出口

出口是房间和房间之间的单向通行的信息。

每个出口包括目的地,指令,条件,耗时。

特别的,以我的经验的话,迷宫是需要代码做一些特殊指令的,不应该通过地图信息来处理。

另外,出口是不包含起点信息的。

因为理论上有一些飞行指令,可以在符合条件(比如室外)的房间,都飞到固定点去(北侠好像只有养飞禽才能,我没用过)。

Cost 耗时

耗时时每一个出口的费用的评估。理论上是最小是1的整数。

寻路就是按照最小的耗时去找路线。有些房间之间虽然指令少,但可能有BUSY等限制,可能绕一下更合理。

条件 Condtion

这是比较核心的一个数据。

有些路径,有门派限制,有些路径,有性别限制。

比如我记得明教女休息室,只有 门派明教,性别女才能进去。

那这时候,我们可以通过给出口加上调教,比如 有 明教 标签 和 女性标签,才能使用,就能利用这个地图数据了。

既然有条件,我们还应该有 白名单和黑名单的概念。即只有某个标签能进入,和只有某个标签不能进。

比如有些房间,丐帮的进去会被踢,那我们就不进去。

这就是基本的标签/条件模式。

更复杂点。

很多地方,能不能通行不光光是 有/没有。

比如要一定的轻功/内力/技能才能通行。

这时候我们可以在代码里生成N个不同的标签,但这太丑了,更不好维护。

所以我们的标签是带数值的标签。

默认的标签和条件就是数值1.

指定数值的话

force:100 指你有100级force
这样你能进入 force:40的出口,但不能进force:200的出口。

这样,配合标签,数值,取反几个操作,我们能解决绝大部分的差异化路线规划的问题了。


北大侠客行Mud(pkuxkx.net),最好的中文Mud游戏!
 楼主| 发表于 2025-5-20 15:13:35 | 显示全部楼层
好,开始来细节了。

首先,就是房间信息的结构

  1. public partial class Room
  2. {
  3.     public const string EncodeKey = "Room";
  4.     public string Key { get; set; } = "";
  5.     //房间的名称,显示用
  6.     public string Name { get; set; } = "";
  7.     //房间的描述,显示用
  8.     public string Desc { get; set; } = "";
  9.     //房间的区域,筛选用
  10.     public string Group { get; set; } = "";
  11.     //标签列表,筛选用
  12.     public List Tags = [];
  13.     //房间出口列表
  14.     public List Exits { get; set; } = [];
  15.     public List Data { get; set; } = [];
  16. }
复制代码
这里面,Key是核心,主键

Name是方便记忆的名字,Desc和代码完全无关,属于注释

Group,分组,基本就是城市了。

Tags就是我刚刚说的带数值的标签,是房间本身的属性,比如非战斗房间,室外房间等。

Exit是一个一个的出口,下一楼贴结构。

Data就是键值对,额外数据。方便代码里有需要时设置一些额外信息。

直观显示时这样


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
北大侠客行Mud(pkuxkx.net),最好的中文Mud游戏!
 楼主| 发表于 2025-5-20 15:15:53 | 显示全部楼层
本帖最后由 jarlyyn 于 2025-5-20 03:18 PM 编辑

出口的数据结构是这样

  1. public class Exit
  2. {
  3.      //路径指令
  4.     public string Command { get; set; } = "";
  5.     //目标房间
  6.     public string To { get; set; } = "";
  7.     public List Conditions { get; set; } = [];
  8.     public int Cost { get; set; } = 1;
  9. }
复制代码
比较简单

Command属于必填,指令
To是目标房间
Conditions是条件,环境(后面详解)的Tag符合条件,才能使用这个出口
Cost是耗时

直观界面大概是这样


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
北大侠客行Mud(pkuxkx.net),最好的中文Mud游戏!
 楼主| 发表于 2025-5-20 15:25:01 | 显示全部楼层
好,最重要的数据体层结构有了。

接下去我们要看应用的数据了。

一般来说,我不建议机器里使用的数据全部是写死的魔数,Magic number。

当应该是预设好的变量。

因此,实际使用的信息,我这里分为4类

1. Marker标注,单点信息,就是一般地图里的图钉,可以理解为别名
2.Route线路,有顺序的房间列表,固定遍历路径。
3.Trace足迹,无顺序的房间集合,一般表示某些道具/NPC可能出现的房间
4.Region区域,无顺序的房间集合,和Trace却区别是这不是实际数据,而是一类房间的归类。

因为这些数据都是给脚本使用的,所以除了常规的Key之外,还有个特殊的字段,叫Message,消息。

举个简单的例子,Npc类的Marker,可以在Message里标准NPC的ID,Name,等,这样把NPC Group的Marker都取出来,就能直接得到NPC列表了。

房间列表之类同理。


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
北大侠客行Mud(pkuxkx.net),最好的中文Mud游戏!
 楼主| 发表于 2025-5-20 15:29:25 | 显示全部楼层
Marker的结构

  1. public partial class Marker
  2. {
  3.     public string Key { get; set; } = "";
  4.     public string Value { get; set; } = "";
  5.     public string Desc { get; set; } = "";
  6.     public string Group { get; set; } = "";
  7.     public string Message { get; set; } = "";
  8. }
复制代码


很简单的结构,Message就是之前提过的传递给脚本的信息

Value就是对应的房间。

Group是你的标记的类型,比如NPC类


这类数据在北侠这种更新特别频繁的MUD里很重要。

比如挖花任务,NPC就从濠州拆迁过。

如果使用Marker这种别面来记录她的位置,那么她哪怕拆迁到美国德州,我们也不怕。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
北大侠客行Mud(pkuxkx.net),最好的中文Mud游戏!
 楼主| 发表于 2025-5-20 15:32:23 | 显示全部楼层
Route 固定路线

  1. public partial class Route
  2. {
  3.     public string Key { get; set; } = "";
  4.     public string Desc { get; set; } = "";
  5.     public string Group { get; set; } = "";
  6.     public string Message { get; set; } = "";
  7.     public List Rooms = [];
  8. }
复制代码


理论上必须有,但我不太常用的一个数据结构了

Rooms是有顺序的房间Key,我为了避免混淆,特地把几个相似结构的房间信息起了不同的字段名。

说真的,固定遍历列表在一个地质变化激烈的MUD里,真没什么用


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
北大侠客行Mud(pkuxkx.net),最好的中文Mud游戏!
 楼主| 发表于 2025-5-20 15:39:58 | 显示全部楼层
Trace 足迹

  1. public partial class Trace
  2. {
  3.     public string Key { get; set; } = "";
  4.     public string Group { get; set; } = "";
  5.     public string Desc { get; set; } = "";
  6.     public string Message { get; set; } = "";

  7.     public List Locations { get; set; } = [];
  8. }
复制代码
Locations是房间ID列表

这个其实是挺好玩的一个结构

他主要标记了可能出现的位置,比如要找行踪不定的托钵僧。

还可以用来记录一个区域内有那些房间的可以挖花。

对于他,我还特地做了一个API,可以给指定的Trace增加房间,这样方便在游街/任务的时候维护响应信息。

甚至在某些MUD里,可以记录任务NPC的出没位置,可以不用搜索整个区域。

当然,这个数据必须配合动态生成路径使用,不然没啥价值



本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
北大侠客行Mud(pkuxkx.net),最好的中文Mud游戏!
 楼主| 发表于 2025-5-20 15:47:09 | 显示全部楼层
Region 地区

  1. public partial class Region
  2. {
  3.     public string Key { get; set; } = "";

  4.     public string Group { get; set; } = "";
  5.     public string Desc { get; set; } = "";

  6.     public string Message { get; set; } = "";
  7.     public List Items { get; set; } = [];
  8. }
复制代码
不好玩,但实用的一个结构。

他和Trace都是无顺序房间集合,但这个的本质是一个查询,取出来后的房间需要缓存。

一个Region包含多个有顺序的RegionItem
  1. public class RegionItem(RegionItemType type, string value, bool not)
  2. {
  3.     public bool Not { get; set; } = not;
  4.     public RegionItemType Type { get; set; } = type;
  5.     public string Value { get; set; } = value;
  6. }
复制代码
RegionItem很简单,3个属性
  • Type 房间还是房间组(城市)
  • Value 具体的值
  • Not 添加还是排除


就是把一系列符合条件的房间,拼成一个房间集合



干说可能很难理解。

配合我那过时的北侠机器的一个数据来说明下吧

  1. 建康府=建康府*,建康府北城,建康府南城
复制代码


就是把几个分组,和额外的房间,拼成一个实际使用的区域。

这样,扫街的代码只需要记录房间对应的Group就行。具体实际使用的路径,通过几个Group合并后,再生成动态路径即可。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
北大侠客行Mud(pkuxkx.net),最好的中文Mud游戏!
您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|手机版|小黑屋|北大侠客行MUD ( 京ICP备16065414号-1 )

GMT+8, 2025-5-30 12:42 PM , Processed in 0.013445 second(s), 16 queries .

Powered by Discuz! X3.4

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表