作者laechan (小太保)
看板mud
標題[閒聊] 傳統物件及虛擬物件
時間Wed Dec 12 21:50:01 2012
傳統物件的特色是
void create()
{
裡面呼叫一些設定函數,將資料設定在 data 變數裡;
}
void init()
{
定義一些動作;
}
動作函數()
{
bla bla
}
然後這樣的物件通常被玩家攜帶在身上,取出的方式是
if( ob = present("物件id", 玩家) )
或者說該物件本身為玩家所存在的環境,取出的方式是
if( ob=environment(玩家) )
或者其它的「實體物件型式」,例如以 find_living、
find_player、find_object、....等取出的物件。
因為資料都在物件裡頭,所以取出時通常是這樣..
ob -> query("xxx")
ob -> query_xxx()
.
.
================================================
傳統物件及虛擬物件的差異,可以用一個例子來說明。
比方某物件叫做 pill,它 add_action 了一個 eat 動
作,可以讓玩家 eat pill。
int eat_pill()
{
玩家吃了藥;
this_object()->remove(); // 讓藥消失
return 1;
}
假設我們這個 mud 有很多可以 eat 的東西,那麼最直
覺的想法就是,eat 不能寫成像 chat、look 等等的「
全域指令」嗎?
於是有了第一種變型寫法,就是 eat 是全域指令
int cmd_eat(string str)
{
if(ob=present(str,this_player()))
{
if(ob->query("這東西可被 eat 的參數"))
{
玩家吃了藥;
ob->remove();
這樣寫的好處,就是「所有的藥」都不需要再定義 eat
動作以及相關的函數,只要寫一個 eat 指令,然後所有
可 eat 的藥都設定可被 eat 的參數,則有設參數的藥
就可被 eat,其效果如同在物件裡面定義 eat 動作一樣
=================================================
那麼,進一步來想,「pill」這個 物件 真的需要存在
嗎?例如改寫上面的東西..
int cmd_eat(string str)
{
// 比方 eat str pill(力量藥丸)
if(this_player()->query("obj/"+str)>0)
{
玩家吃了藥;
this_player()->add("obj/"+str,-1);
.
.
換言之,玩家取得了藥或買了藥:
this_player()->add("obj/"+str,1 或 n);
玩家吃了藥或賣出了藥或將藥給人:
this_player()->add("obj/"+str,-1 或 -n);
if(this_player()->query("obj/"+str)<1)
this_player()->delete("obj/"+str);
這就是第二種變型寫法,也就是「藥」這個物件實際
上已經不存在了,它變成只是一個「資料stream」,
只存在著被加減、設定及刪除等特性,我稱此為虛擬
物件。
當我們的 mud,很多的實體物件都能被虛擬物件所取
代時,它達到的最直接效果就是物件減量,我們不僅
可以省去「創造」物件的時間,還能更快速地處理一
些動作(如喝藥水)。
==============================================
假設我們把 str pill(力量藥丸) 給虛擬化了,那麼
我們可能會寫一個指令來列出我們身上到底有哪些這
類的物品..
> obj
力量藥丸 138顆 生命藥水 144瓶
內力藥水 50瓶 蘋果 12顆
.
.
今天我們有了一個 eat 指令可以吃力量藥丸了,進一
步來說,我們或許也想 view 看看力量藥丸究竟係啥
米碗糕。
寫法有非常多種,這裡只舉一種,就是定義「資料集
物件」,例如..
mapping obj_data=
([
"str pill":(["name" : "力量藥丸",
"unit" : "顆",
"desc" : "這是一顆力量藥丸,吃了(eat)之後力量會增加喔!",
"value" : 5000,
.
. ]),
"hp pot" :(["name" : "生命藥水",
"unit" : "瓶",
"desc" : "這是一瓶生命藥水,喝了(drink)它可以回復生命值。",
"value" : 1000,
.
. ]),
.
.
]);
那麼,當我們 view str pill 時,就去呼叫上述的物件
取得 obj_data["str pill"] 的所有資料,自然就能得到
下列結果
> view str pill
obj_data["str pill"]["name"]
==========================================
英文: str pill
單位: obj_data["str pill"]["unit"]
價錢: obj_data["str pill"]["value"]
敘述: obj_data["str pill"]["desc"]
==========================================
這就是第三種變型寫法,也就是所有虛擬物件的資料,其
實都集中存放於一個物件的 mapping 資料裡頭,有需要
的時候就去讀出即可。
這樣既可確實減少物件的數量,但同時又能令虛擬物品具
有「實體感」。
==================================================
再來的話,既然可以有「虛擬物品」,那能否有「虛擬村
民」呢?比方說我們要列出房間所有的物品時是這樣做的
obs = all_inventory(room);
foreach(ob in obs)
{
if(userp(ob))
msg += ob->query("short")+"正站在這裡。\n";
else
msg += ob->query("short")+"\n";
}
那假設我們讓虛擬村民的列表排在前頭,那麼我們就可以
這樣幹..
vobs = room->query("vobs"); // 讀取房間的虛擬資料
foreach(vob in vobs)
msg += 虛擬資料物件->query_vob_name(vob); // 讀取該虛擬物件的名稱
// 然後才做實體物件的列表
obs = all_inventory(room);
foreach(ob in obs)
{
if(userp(ob))
msg += ob->query("short")+"正站在這裡。\n";
else
msg += ob->query("short")+"\n";
}
這樣村民就會先被列在前頭。而所謂的 look 村民的動作
就套用上面讀取村民 desc 的應用即可。
這個適用於「村民通常沒啥用途只是擺著好看」的 mud,
它可以確實減少村民物件的數量。
甚至,如果想寫的更完善一點的話,還可以實現村民的「
loop speak」、「random moving」、「cound ask」....
這些都可以用虛擬物件來實現,而且都存在著多種寫法。
此即虛擬物件的變通應用。例如聖殿目前使用的 quest 任
務系統(以任務腳本為主體)即是其變通應用的一種。
虛擬物件的好處在於可以省去許多 ob->query/set/add/..
或是 ob->query_xxx/set_xxx/add_xxx 的物件資料存取動
作,直接做本地式的資料存取,「有需要」時才去讀取該
物件的其它資料即可,例如:
view 時 -> 才去讀 desc
sell 時 -> 才去讀 value
.
.
以上,一點心得跟大家分享。
Laechan@Sanc
--
※ 發信站: 批踢踢實業坊(ptt.cc)
※ 編輯: laechan 來自: 210.61.157.53 (12/12 22:06)
推 kyoe:我還以為是 virtual object= = 12/12 22:39
→ laechan:嗯? 12/12 23:33
推 bnn:推 12/13 03:16
推 fishsquare:push 12/13 04:27
推 Jate:這種方式在給其他低階GM撰寫區域時, 不會造成困擾跟限制嗎? 12/19 10:07
我們假設該 GM 已經寫好區域及怪物,只差「掉落物品」沒有
設定,以我自己在聖殿為例
/u/l/laechan/area/newsnake/room // 區域檔存放位置
/u/l/laechan/area/newsnake/mob // 怪物檔存放位置
> areadata short
snake1.c 一群啪啦啪啦蛇(Snake) [13條蛇]
snake2.c 一群唏咻唏咻蛇(Snake) [13條蛇]
snake3.c 一群波米波米蛇(Snake) [13條蛇]
目錄下共有三種怪物,我想設計讓它們都會掉蛇鱗、蛇牙以及
蛇尾,那我就先設計好三種虛擬物品。
> vobjs -filter name = 毒蛇
編號 名稱 設定者 單位 性 質 攜帶 價錢 賣店 交易
============================================================================
m003 毒蛇的鱗片 laechan 片 怪掉落 99 100
m004 毒蛇的牙齒 laechan 顆 怪掉落 99 300
m005 毒蛇的尾巴 laechan 條 怪掉落 99 500
============================================================================
設定好之後,再設定「怪物掉落虛擬物品」,如下..
/u/l/laechan/area/newsnake/mob: list
([ "snake1" : ([ "m003" : 500, "m005" : 200, "m004" : 300 ]),
"snake3" : ([ "m003" : 500, "m005" : 200, "m004" : 300 ]),
"snake2" : ([ "m003" : 500, "m005" : 200, "m004" : 300 ]) ])
其中 500 代表掉落機率 50%。(這時 1 就代表 0.1%),
"m003" : 500 就代表鱗片掉落率 50%。
從這時起,玩家到這個區域打這三種怪物,就會掉鱗片、
牙齒及尾巴,而且不需要更動到怪物檔。
而某一天例如我們想調整牙齒的價格時,使用 vobjs 指
令調整即可,價格可馬上套用;又某一天想變更 snake1
的牙齒掉落率為 40% 時,用 vobjs 指令同樣可馬上變更
並套用。
它的好處就是擺脫傳統「掉落物」的設定方式,將物品改
為虛擬(即實際上並沒有鱗片這個物件存在),好更新、好
管理、而且其更動具有即時性。
> call snake;die
一群啪啦啪啦蛇發出一聲慘叫!!
一群啪啦啪啦蛇慢慢的倒在地上死了...你得到 5346點的經驗值。
你取得了一個毒蛇的鱗片。
再使用聖殿的 ob 指令即可進行觀看及商店販售
> ob
你的物品欄帶著 4/99 種物品:
─────────────────────────────────────
1.毒蛇的鱗片 ( 36) 2.測試物品 ( 99) 3.毒蛇的尾巴 ( 2)
4.毒蛇的牙齒 ( 15)
> ob sell 36 毒蛇的鱗片
你賣掉 36 片毒蛇的鱗片獲得 3600 影特幣。
換言之,可依自己 mud 的需要,看是只寫出虛擬物品用
來讀值就好,或是搭配怪物掉落的設計、掉落物販售交易
的設計都可。
然後其最終目的,都是為了讓 wiz 「只需透過設定而非
撰寫 code」的方式來配置怪物掉落物。
※ 編輯: laechan 來自: 210.61.157.53 (12/19 11:42)
→ laechan:聖殿目前從區域繪圖到區域完成,通通只需做設定不需寫code 12/19 11:44