作者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