作者laechan (小太保)
看板mud
标题Re: [闲聊] 区域产生器
时间Sun Nov 6 15:25:07 2011
※ 引述《laechan (小太保)》之铭言:
: 首先来一张简单的 m x n 的地图(底下是 7x7)
: x-x-x-x-x-x-x
: | | | | | | |
: x-x-x-x-x-x-x
: | | | | | | |
: x-x-x-x-x-x-x
: | | | | | | |
: x-x-x-x-x-x-x
: | | | | | | |
: x-x-x-x-x-x-x
: | | | | | | |
: x-x-x-x-x-x-x
: | | | | | | |
: x-x-x-x-x-x-x
string str,*tmps=([]);
int i,n,n1,n2;
sscanf(str,"%d-%d",n1,n2);
n=(n1*2-1)*(n2*2-1); // 计算需产生的数量
tmps=allocate(n); // 事先配置
for(i=0;i<n;i++)
{
if( (i/n1)%2==0 ) // x-x-x- 行
{
if( i%2 == 0)
tmps[i]="x";
else
tmps[i]="-";
}
else // | | | 行
{
if( i%2 == 0 )
tmps[i]="|";
else
tmps[i]=" ";
}
}
str="";
for(i=0;i<n;i++)
{
str+=tmps[i];
if( (i+2)%n1 == 0) // 换行
str=str+"\n";
}
write(str+"\n"); // 秀出来确认产生的图是不是对的
: 先随机戳几个洞,就变如下..
: x-x-x-x x-x-x
: | | | |
: x-x x-x-x
: | | |
: x-x x-x-x
: |
: x-x x-x-x
: | | | |
: x-x-x-x-x x
: | | | | |
: x-x-x-x-x-x
: | | |
: x x-x-x-x
随机戳几个洞的写法很多,底下展示一种
n=n1*n2;
h=random(n); // 先跑出要把第几个 x 挖掉
// 第几排 排尾 在哪一排的第几个位置
h= (1+(h+1)/n2) * (n1*2-1) + ((h+1)%n1)*2-2;
/*
假设是 3x3, n=25, 假设跑出的 h 是 4(代表中间的 x)
则新的 h = (1+(4+1)/3) x (3x2-1) + ((4+1)%3)x2 -2)
= 2 x 5 + 2
= 12 (tmps[12] 刚好就是中间)
*/
tmps[h]=" "; // 挖洞
当然你也可以指定 tmps[12] = " ", 看是要随机还是手
动挖都可以, 手动的好处是不局限 "x", 你也可以把 "|"
或 "-" 挖掉。
: 然後把地图编号一下
: 001-002-003-004 005-006-007
: | | | |
: 008-009 010-011-012
: | | |
: 013-014 015-016-017
: |
: 018-019 020-021-022
: | | | |
: 023-024-025-026-027 028
: | | | | |
: 029-030-031-032-033-034
: | | |
: 035 036-037-038-039
int r=1;
n=sizeof(tmps);
// 第一步: 先处理 x
for(i=0;i<n;i++)
{
if(tmps[i]=="x")
{
if(r<10)
tmps[i]="00"+r;
else if(r<100)
tmps[i]="0"+r;
else
tmps[i]=""+r;
r=r+1;
}
}
// 第二步: 再处理 | (-不需处理)
for(i=0;i<n;i++)
if(tmps[i]=="|")
tmps[i]=" | ";
// 第三步: 再处理 " "
for(i=0;i<n;i++)
if(tmps[i]==" ")
tmps[i]=" ";
: 接着依这个地图产生出区域房间..
: > ls
: 1 001.c 1 007.c 1 013.c 1 019.c 1 025.c 1 031.c 1 037.c
: 1 002.c 1 008.c 1 014.c 1 020.c 1 026.c 1 032.c 1 038.c
: 1 003.c 1 009.c 1 015.c 1 021.c 1 027.c 1 033.c 1 039.c
: 1 004.c 1 010.c 1 016.c 1 022.c 1 028.c 1 034.c
: 1 005.c 1 011.c 1 017.c 1 023.c 1 029.c 1 035.c
: 1 006.c 1 012.c 1 018.c 1 024.c 1 030.c 1 036.c
r=r-1;
for(i=1;i<=r;i++) // 看最後产生几个房间
{
if(i<10)
files=__DIR__+"00"+i+".c";
else if(i<100)
files=__DIR__+"0"+i+".c";
else
files=__DIR__+i+".c"; // 产生要写的档案
file_text=FILE_TEXT; // 读入事先编好的房间档
exits=get_exits(i); // 依据 i 去读出这个房间有设哪些出口, 连接哪个房间
foreach(tmp in keys(exits))
file_text+="\""+tmp+"\":\""+exits[tmp]+"\",\n"; // "east":"/d/xxx",
file_text+="}\n";
write_file(files,file_text);
}
FILE_TEXT 的定法类似底下
#define FILE_TEXT "#include <mudlib.h>\ninherit ROOM;\n\nvoid create...."
get_exits 则看各家的写法,基本上就是做座标判读,判断
第 n 个房间的上下左右有没有连结,以左右的判断为例
// 假设 tmps[n] 是房间
if(n>0 && tmps[n-1]=="-") // 代表左边有连结房间
if(n<(n1*2-1)*(n2*2-1)-1 && tmps[n+1]=="-") // 代表右边有连结房间
if(n>n1+2 && tmps[n-(n1+2)]=="|") // 代表上面有连结房间
if(n<(n1*2-1)*(n2*2-1)-n1 && tmps[n+(n1+2)]=="|") // 代表下面有连结房间
重点就是要做边界判定。
上面是大致的概念,用「移动磁头」的概念,当我们想要对
第 n 个 x 做操作时,第一步就是计算出相对的座标出来..
经过计算
由n ───→ 得出相对的 tmps[m]
所以建议将部份计算函数化..
1. 由 n 得到 m 的函数
2. 判定边界的函数
3. get_exits
4. 秀图程式(随时确认修改後的 tmps 是否正确)
以上一点心得。
LAechan
--
※ 发信站: 批踢踢实业坊(ptt.cc)
◆ From: 111.253.169.46
1F:→ Searle:对这种有兴趣的可以去看RW的code 11/06 16:51
2F:→ laechan:我记得以前 doom 也有 imm 讨论过相关的设定, 另一个设定 11/06 18:49
3F:→ laechan:是「如何在 mud 呈现一个 100x100 格的区域」 11/06 18:49
4F:推 pssjim:FS狂想空间有个殭屍洞是类似的区域,有空可以去问一下喔 11/07 14:10
5F:推 jaykill:pss不上线还在这边喇赛 11/07 21:24
6F:→ laechan:呵这应该是在各 mud 均列为机密的东西吧 11/07 21:56
7F:推 Picoro:以前有写过类似的程式 ... 不过我是反过来的作法 ... 11/09 02:33
8F:→ Picoro:我是先随机决定好两个点或多个点为出口 11/09 02:33
9F:→ Picoro:之後随机放置障碍物 , 开始乱摆 11/09 02:34
10F:→ Picoro:摆完後我是使用EDA用的routing演算法"flooding algorithm" 11/09 02:35
11F:→ Picoro:从终点开始一直跑 , 直到它可以连结到起点 11/09 02:35
12F:→ Picoro:之後再用随机方式 , 看要它继续淹几次 11/09 02:36
13F:→ Picoro:若一直没有找到起点 , 但所有结点淹不下去 , 11/09 02:37
14F:→ Picoro:那就重新摆障碍物 , 再淹一次 , 一直到有路连通起终点 11/09 02:37
15F:→ Picoro:由於淹的过程已经建立好连结的指标 , 因此写档也很快 11/09 02:38
16F:→ Picoro:但我最大的问题还是在叙述很难写 ... 所以就不鸟鸟之 Orz 11/09 02:39
17F:→ belion:应该也能将叙述用此类作法.? 11/09 09:40
18F:→ laechan:做法有很多种可行 11/09 12:54
19F:推 kyoe:叙述真是大家心中的一个痛阿!! 11/09 15:47
20F:推 Picoro:毕竟MUD是文字叙述的东西 , 如果是图型 , 或许会简单许多 11/09 16:18
21F:推 neoyori:图形更困难吧,就算像RPGMaker那样有素材给你贴也要贴超久 11/10 00:32
22F:推 Picoro:不过如果每一格都可以用像FC时代的贴图 , 看起来还ok的感觉 11/10 01:53
23F:→ Picoro:毕竟以自动化设定区域来说 , 随机选素材来贴应该比叙述好用 11/10 01:54
24F:→ Picoro:不过没有实际接触那种东西 , 有些可能是我没看到的东西 11/10 01:55
随机叙述还有一种做法,适用於发展已久的 mud,它的做法
同样是建立资料库,但是建立的标的是「选定的房间」,例
如说底下三个房间....
/area/room/snake/009.c 毒蛇地穴 -
你看到在洞穴内长着一些从没在其他地方见过的蕈类, 这些蕈类
的上头有着一点一点暗红色的圆形斑点, 据说这些蕈类都含有剧
毒, 吃了马上灵魂出窍.
/area/room/spipder/008.c 千蛛洞
洞穴内一片黑暗, 而且你感觉到你脸上与身体上都黏上了蜘蛛丝
, 感觉怪讨厌的.
/area/room/scorpion/003.c 天蠍洞
里头屍骨遍布满地, 残破的衣服上还沾满着血迹, 可能是在与毒
物战斗时死去的冒险者吧! 因为在衣服上还有奇特的黑色液体残
留着.
假设这类的房间搜集了约一两百个,建成的资料录如下..
string *room_files=([]);
room_files=({
"/area/room/snake/009.c",
"/area/room/spipder/008.c",
"/area/room/scorpion/003.c",
.
.
.
});
那基本做法有两种..
一、进入一个房间时,该房间的叙述「随机取上述房间的其中一
个」
string query_long()
{
string files=room_files[random(sizeof(room_files))];
object room;
if(room=find_object_or_load(files))
return room->query("long");
}
二、搭配三段叙述法
string get_msg(object r1,object r2,object r3);
string query_long()
{
int s;
object room1,room2,room3;
room1=find_object_or_load(room_files[random(s)]);
room2=find_object_or_load(room_files[random(s)]);
room3=find_object_or_load(room_files[random(s)]);
return get_msg(room1,room2,room3);
}
string get_msg(object r1,object r2,object r3)
{
string *m1,*m2,*m3,tmp,msg;
int i,j;
m1=explode(r1->query("long"),", ");
m2=explode(r2->query("long"),", ");
m3=explode(r3->query("long"),", ");
tmp=sprintf("%s, %s, %s.",m1[0],m2[1],m3[2]);
msg="";
j=strlen(tmp);
// 每 28 个中文字一行
for(i=0;i<j;i=i+56)
msg+=tmp[i..i+55]+"\n";
return msg+tmp[i..j-1]+"\n";
}
其中第二种做法只要做好资料前处理其实就是三段叙述法,
以这种方式形成的房间叙述有时会像底下这样...
xxx.c 某房间
你看到在洞穴内长着一些从没在其他地方见过的
蕈类, 而且你感
觉到你脸上与身体上都黏上了
蜘蛛丝, 可能是在与毒物战斗时死
去的
冒险者吧!
问题:像这样
完全不知所云的叙述真的可以吗?
我只能说,嘛,圣殿是可以的。圣殿有好几千个房间的叙述
都是许多巫师认真撰写下的产物,从里面取样产生新的叙述
并不困难,圣殿的[新区域]以这样的方式产生叙述完全没有
问题,
因为根本没啥玩家在看房间叙述,这才是让我兴起想
写随机地图、随机叙述、甚至随机怪物分配等工具或继承用
物件的原因,这样就不需要再徵新巫师、也不需要再向玩家
徵求相关的东西,更不需要忍受绞尽脑汁写的叙述被蹧蹋的
不爽感。
(当然实际上我还是会要求产生的叙述要够正常)
後 mud 时代,我觉得 coding 人员的时间应该花在重点上,
而不是花在这类纯资料的构思上,我计划让圣殿明年至少增
加30个以「随机」为主架构的区域,大部份是区域的附属子
区域(如OX洞窟、OX塔)。
※ 编辑: laechan 来自: 122.117.11.103 (11/10 05:45)
25F:推 dannielz:推没啥玩家在看叙述 尤其是这种随机生成的地图 11/10 15:02
26F:→ dannielz:除非带有解谜提示性质 否则没什麽必要在叙述上钻牛角尖吧 11/10 15:03
27F:→ kruz:diablo刚出来的时候就有人写过类似的,後来有发过paper的样子 11/11 03:43
28F:→ kruz:99年左右的mud研讨会的时候也有发表过. 11/11 03:43
29F:→ kruz:翻了一下,应该是第一届研讨会就发表了,所以是96 or 97的样子 11/11 03:48
30F:→ kruz:实作应该是在ES2上有跑过 11/11 03:49