Sunday 13 April 2014

MMD: 2

上一章

「啵!」一聲響,有人在遊戲店外開香檳。

「這支酒,本來打算回到家後,觀看直播時開。現在雖然趕不及回去,但既然大家都在此一同見證伺服器開張,這支酒就由大家分了吧!」隊伍較前方,穿裇衫西褲的人說。懸浮頭上的虛擬標籤顯示他的名字叫域佗。「不用客氣,我們買了紙杯。」一雙皮鞋在混凝土上咯咯作響,辦公室女郎從對面街返回隊伍。「伊莉沙貝……」本納不禁念起這個少見的名字。

原本有秩序的隊伍頓時堆成一團;吸引這班人的到底是酒還是美女,本納不知道,不過觀乎這班人的年齡,大概兩樣都有。能在星期二中午到商店排隊的,大多都是學生,不過現場好似無人理會法律問題;二人不分年齡地分發,眾人亦不分年齡地乾杯。「一群小鬼。」本納在意的,只是這場騷動到底會延誤他領取頭盔多久。

「這麼高興,如果各位未用膳的話,共進午餐慶祝如何?我們請客。」 現場還餘幾十人。先不說餵飽這班化骨龍要多少錢,要找個容得下他們的的地方也有難度。不過,這兩個從辦公室偷懶出來的人好像不太在乎。二人談兩句後,伊莉沙貝又再離隊。

域佗從遊戲站走出來後,對隊伍說:「各位,有意聚餐的話,就到下一條街的褔來酒家吧。…」手中的袋載有好幾個頭盔,「…說是姓梁三十位就可以。…」臨走前再加一句:「…不懂路的人請打開眼鏡,我會廣播地圖給你們。」 本納住在這區,自然知道附近的酒家不出幾間。以單車的話,就算全都走一遍也不過十分鐘,所以他本無意接收廣播。不過,因為眼鏡設置為自動接收模式,所以他仍然接到地圖。

到本納取得頭盔之時,已近下午二時。 「反正已錯過開幕禮,現在趕回家也沒用,倒不如去食一餐免費的吧。」 從遊戲店所在的街轉個彎就已到酒家,其實不廣播地址也可以。不過對於不熟悉這區的人,收到地圖是最簡單的方法。

本納到酒家時,從遊戲店走來的人已坐了五圍。對於有人請客這回事,大家還真不客氣。也許是因為午飯時間已過,酒家內「機民」以外的顧客並不多,就像域佗和伊莉沙貝包了場一樣。雖然人少,但酒家仍然嘈吵得像滿坐一樣。看來,雖然新生代不常來酒家,但傳統這回事確實難以擺脫。要說為什麼一眼就能看出哪檯是機民的話,是因為人人腳邊都有起碼一盒傳感頭盔;有人甚至急不及待,已戴上頭盔,在酒家玩起紀道域來。考慮到流動網絡十年不變的延遲值,應該說這班人為玩遊戲能克服一切,還是該說紀道域的網絡延遲補償做得相當出色?

「歡迎,請隨便坐。…」看見幾個機民進來,伊莉沙貝像侍應生般招呼他們。本納坐到尚未滿坐的一檯。這檯上的飯菜比較少,應該是因為人比較遲到;本納認得剛才排隊時見過的幾個臉孔和名字。「…食物和飲品都可以隨便點,…」伊莉沙貝靠近餐桌,小聲說:「…但可以的話請不要點酒精類飲品,否則餐廳可能會有一兩句跟我們說。」向眾人使個眼色。

不遠處,真正的侍應生目本轉睛地望向這邊;仿佛稍一分神,這邊就會如脫韁野馬般。似乎在本納來到前,這裏發生過些什麼重要的事。

域佗和伊莉沙貝未停過跟眾人說話,在檯間穿梭來往。趁二人都不在這檯時,「那兩人真好客得不得了。…」坐在本納旁的戴維斯說:「…你們覺得他們像婚宴上的新人嗎?」戴維斯語氣像在自言自語,不太期望會有人回應。「要說的話,倒比較像小學旅行團的領團老師。」本納說,語氣比戴維斯更像自言自語。

不過,音量再低,戴維斯還是聽到:「……也對。」說是小學未免過份,在坐最小的怎看也起碼是初中生,不過就氣氛而言,戴維斯也大致同意本納的看法。

面對本納的冷嘲,戴維斯沒有顯現如一般人的反應;本納略感意外。「學校旅行嘛,那些日子早已離我遠去了。」戴維斯感嘆。「在緬懷學生年代嗎?」「我的樣子有那麼老嗎?我來年才大學二年級。」原來是大學生,難怪思想較為成熟。單看看貌還真看不出。

「不是,只是香港的大學難進;統計上,你不是大學生的機會較高。」「原來是這樣。又好像幾合理。你呢?到了緬懷學生年代的年紀了嗎?」「明年才大三;不過已經開始緬懷中學時代了。」本納反射性地拿起茶杯喝一口。

不欠,這次輪到域佗來到這檯:「食物合口味嗎?你們這年紀的人也許不常到酒家,但可以容納到這麼多人的地方實在不多。」「食物很好,多謝。」戴維斯有禮地回應。「即使不好,我們也不敢太多怨言。」本納一如以往地直言。

「那就好。我們在討論,不如組織一個公會。如果成事的話,你們會考慮加入嗎?」「這主意不錯。」「但我們全都是新玩家。如果可以找到測試玩家就好。」「不用擔心,我們有同事是測試玩家。不過據他說,紀道域正式推出時會重設伺服器數據,所以測試玩家能帶給新玩家的不多。」「不要緊,請務必預我一份。」「我也是。」「加一!」這檯的反應跟其他的一樣湧躍。

「一定。我們還未想到公會的名字,現在正收集意見,如果你們有提議的話請隨便提出。原本我們想用《龍門酒樓》來反映這公會的開端,不過人人都認為這名字太柒。」「他們也未免太直接了……」「我不怪他們。」域佗笑着說。說實在,本納也不怪他們;如果他聽到有人想用這個名字,他大概也會直言感想。

「『龍』字或可保留。雖然有點俗套,但至少能保留公會起源的含意。」戴維斯說。「《龍窟》如何?取其『此處有龍』的危險之意,就如中世紀地圖中未知領域所寫的。」本納道。

「這個不錯。…」域佗轉身:「…喂,伊莉,我們有名字了!」跟伊莉沙貝說兩句,又指一下本納。看二人的表情,似乎他們也頗喜歡這名字。

沒有人再走進酒家後,域佗和伊莉沙貝終於肯坐下來吃飯,但不久後,「你們繼續吃,我們有事要先走。」伊莉沙貝又再起身。「如無意外,遊戲內我們都會用現在使用的名字。遊戲內見。…」到收銀處交帶付款細節後,二人離開酒家。「…記得加入《龍窟》!」

下午三時,本納也離開酒家。因為吃得很飽,踏單車時不敢太使勁,所以原本回程的五分鐘路程變成足足十五分鐘。本來他並不打算吃得這麼飽,但他那檯人點菜實在點得太多;鄰桌的人又比他們早到,幫不了忙。最後,他們還是要靠打包才能清掉桌上的食物。現在想起來,一早將食物打包或者才是正確選擇。

回到房後,本納立即拆開傳感頭盔的包裝,戴上頭盔。眼鏡馬上就偵則到頭盔,而且順利安裝驅動程式,全程毋須人手介入;一分鐘後,頭盔已處於能夠使用的狀態。

頭盔比本納想像中的輕巧得多。在紀道域開發初期,本納見過長春藤所用的工程樣本頭盔。那活像戰機機師所用的頭盔,非常龐大;與其相比,本納現在所戴的零售版頭盔則似薄身的單車頭盔,輕盈得多。工程樣本和零售版有兩個主要分別:第一,零售版是設計成眼鏡的附加裝置,所以耳邊和眼附近都留有明顯空位。第二,工程樣本為求方便除錯,將系統盡量簡化,所以是以接線連接主機的,零售版則採用無線方式。

本納還記得小時候,當「眼鏡」還是叫做「智能眼鏡」時,眼鏡只是其他電子產品的附加裝置;現在居然輪到眼鏡有自己的附加裝置。本納不禁慨嘆時代變遷。他看看房內的瑩幕,推算到底有多夠沒有開啟過部台電腦。

設置好頭盔後,接下來的就是更新遊戲。還好本納早已預購並預載紀道域在電腦上,現在只需要下載少量更新;否則,現在才開始下載整個遊戲的話可能三小時後也未能開啟遊戲。

「本産品啟動時,使用者身體肌肉活動會受限制。基於安全和舒適理由,使用本産品時,建議躺在床上。」頭盔的包裝上有這樣一句。本納如建議所說,睡到床上,合上雙眼,下達開啟紀道域的命令。

Wednesday 12 June 2013

MMD: 1

上一章

陽光刺眼,像在催促尚在床上的本納起床。不過,本納大被蓋頭,對太陽的呼喊無動於衷。

太陽改變策略,轉用隔着被舖都能發動的攻勢,將床面加熱到三十二度。攻擊非常有效,本納不敵高溫,在被中大汗淋漓。為躲避攻擊,本納掀開被子,但如此一來,就中了太陽的計。陽光穿過眼皮,刺上本納的視網膜。

最終,本納投降,起身了。他看看時鐘,「今天終於來到。…」還未到十二時。「…離伺服器開放還有一小時多。」時間尚算充足。

梳洗過後,本納乘上單車,到六個街口外,社區中唯一的遊戲銷售商。自二十年前電子分銷普及後,擁有門市的遊戲銷售商就不斷減少,很多地方甚至一間也不剩。還好這裡還有一間,不然「傳感頭盔」就經網上才能買到。靠郵遞公司十年如一日的服務,會錯過伺服器開張機乎是必然的事。

還未接近銷售點,就已看見一條人龍,本納希望龍頭會探進其他商店中。可惜,來到遊戲銷售商的門市,可以清楚看見人龍確是源自那裏。「天啊。」這麼多人到遊戲銷售店,大概電子遊戲面世以來都從未試過。他們都來做什麼?怎樣都好,都應該與我無關吧。

步入店內,「請問……」店內職員都手忙腳亂,沒有人有空去理會新進來的客人。當本納終於找到空位,截停一個手中沒拿貨物的職員時,「排隊吧。」還未開口,職員就不耐煩地回應,就像今天在本納之前已有二百個人問過相同問題。「但我只是來領取預購的……」「誰人不是?你以為是什麼召集這班人?…」以拇指指向人龍:「…排隊吧。」不等回應就再度投入工作。

環顧四周,本納這才發現,店舖內能放貨物的地方和不能放貨物的地方都在放同一款箱子。「對,還有什麼會引來這班人?」

回到店外,遙望勉強見到的龍尾。排隊人數鐵定過百。「唉……這回一定趕不及開幕。…」時間已近十二時半。「…人何以總要到留待至最後一刻才行動?早一點領取不就能避免這問題嗎?」雖然這句話也適用於其他人,但本納是在說給自己聽遠多於給別人聽。

心已死的本納帶上眼鏡,期望能找到有串流頻道直播開張。豈料不用搜尋,一開機就已看見眾多相關消息。遊戲網站、新聞頻道、個人網誌,全世界都在討論紀道域這款遊戲。

誇讚紀道域會如何改變遊戲界的頻道,一概關閉。自紀道域的概念首次提出以來,都不知聽過幾多遍。無論該頻道有無表述過,觀眾已聽過這論調上百遍;現在還重覆的人實在一點傳媒觸覺也沒有。

正在收看的幾個頻道突然播放同一畫面,原來大家都在直播的開發者的情況。臨近開幕,開發者「長春藤製作組」的辦公室空閒得出奇。多數房間都沒亮燈,而人則集中在辦公室中央的共用空間。大概是能做的準備功夫都已做完,接下來就只能聽天由命,祈求機器如運作如常。

「歡迎大家來到長春藤。…」鏡頭前,年約二十的少女說:「…我是長春藤的創辦人之一,亞莎,也是紀道域的美術總監。紀道域是我們長春藤所製作的第三款遊戲。之前的兩款,你們可能連名字都未聽過。所以,紀道域和長春藤能有今日的規模,我們幾個創辦人至今仍然覺得難以置信。」

亞莎的講話對像,是面前的十幾位記者﹑遙距報導的串流頻道台主,以及全球數以千萬計的觀眾。比起直接面對十億人,面對一千萬隱形人的壓力要高出幾倍,更不要說這片段會被錄影和永遠保留,也不要提面對群眾本就不是她的工作。就算講話者說話生硬,儘管用詞不精準,即使那段話是照讀一早寫好的稿也好,都情有可原。

「稍後我們將有伺服器的開幕儀式。在那之前,現在或者先參觀一下幕後的製作環境,即這個辦公室,以及請製作人員解答你們的問題吧。…」亞莎開始習慣環境,話語間的抑揚頓挫漸漸回復至正常水平:「我們目前所在之處,凡進入辦公室的人都會首先會踏入。這是辦公室的共用空間,亦是會議的首選地點。這邊是編程部﹑美術部,另一邊是測試部,還有休息室…」亞莎指着周圍沒開燈的玻璃房,逐一說,「…來,我們先到編程部。」

亞莎帶記者和全世界參觀各個工作空間。瞬間,長春藤製作組榮登為本納的理想工作地點;相信有幾千萬人都抱相同想法。

打開編程部的燈後,眾人走進無人的辦公室。這裏的結構十分奇怪,落地玻璃好像被亂擺般站立在地上。據亞莎說,編程部中無論職位高低,人人皆有獨立房間;看似亂擺的房間是了令房間都能從窗戶採光。「因為編程人員坐在辦公室的時間遠多於用在走郎的時間啊。」

在角落,一列房間的玻璃間格並不是透明,而是磨砂狀,而且從房間中透出光。亞莎敲一下第一間房的玻璃牆,「方便出來嗎?」房門打開,一名女子從後探出頭來:「什麼。」亞莎對鏡頭說:「這是我們的網絡工程師,珍娜。」「要看我們的工作情況是可以,但不要妨礙我們。你想臨開張才出現網絡問題嗎?」轉身關門。「她的隊伍是負責網絡維護的。大家在登入時如果遇到問題,可以怪罪到她頭上哦。」在珍娜令下,磨砂玻璃變得透明,可以看見她就正與辦公桌對面的人對電腦螢幕指手劃腳地討論;電腦螢幕上正顯示地界地圖,以及不同國家之間的連線。

亞莎想再敲玻璃牆,但螢幕上旋即以大字顯示「我們在討論更新遊戲時其中一個伺服器斷電時如何解決可能出現的不同步問題。不要問,問了你也不會理解答案」。見狀,亞莎唯有住手:「……我們繼續參觀其他地方吧。」

接下來是美術部。與編程部相比,這裏雖然一個人也沒有,但人氣反而比較多,或者是因為個人物品比較多的緣故。房間的佈置和設備的裝飾,幾乎間間房都不同,可看出長春藤不介意員工把公司財産當成私人物品。美術部的設備,無論是影像還是音效相關,都是無與倫比,長春藤甚至有自己的錄音室。「不過大部份設備只是暫時租回來的噢。希望我們終有一日能買下它們吧。」

樓層的另一面是測試部。這邊給人的感覺差不多,同樣是由不規則的玻璃房組成,不同的是一個間房是由二人小隊擁有。

剩下不見窗戶的地方,長春藤將其分作幾作幾個會議室以及休憩空間,並以光纖引入自然光。會造出這種細節,設計者簡直當這裏是住宅一樣。最神奇的是,將一層分作超過五十間房,竟然仍有近九成房間有窗。引發奇蹟的這種執著,就如同看見自然景觀和陽光是基本人權一樣。

回到共用空間時,約是二十三時四十五分。「參觀就到此為正,不過我們還有少許時間,不如由我們解答各位的問題吧。對這辦公室有問題嗎?對我們的工作有疑問嗎?還是想問有關遊戲的問題?什麼也可以問噢。」

「為什麼選擇製作大型多人線上遊戲?這遊戲類型在十年前就不合時宜了。」「我們一直想在遊戲內建立社會。人是群居生物,想跟別人交流實在必然。若擴展這概念,無可避免要建立社會。我們認為,以大型多人線上遊戲為媒體,最能提供建立社會的基礎。」「但大型多人線上遊戲已末落多年,為什麼你們會冒險選擇這個類型,而且還有這樣大的制作?」「一來,這是我們一直想製作的遊戲;二來,我們除冒險外別無他法。有多少獨立製作組能靠製作主流遊戲維生?零,歷史上一個都無。雖然我們已經不算是獨立製作組,但主流遊戲始終是大型製作組和發行商的專利。我們沒有他們的名牌、沒有他們的忠實擁躉;要給納玩家就只能突圍而出。其實大型多人線上遊戲並沒有衰落;衰落的只是大型單人線上遊戲。早在這類遊戲衰落之前,製作商已越來越省去『多人』的元素。當多人線上遊戲減去『多人』,剩下的就只是一般的單人遊戲,還有高出別人多倍的製作費。那麼,沒人再造也是預計之內的事。」

「全球玩家同時上線的負荷,你們的伺服器如何應付?」「我們有眾多伺服器,遍佈全球,分別在日本﹑俄羅斯﹑澳洲﹑香港﹑印度﹑土耳其﹑南非﹑瑞士﹑英國﹑美國﹑和巴西。以這數量,伺服器不難應付負載。」「那『一個世界』的賣點又如何?是放棄了?」「怎會呢。『一個世界』一直是紀道域的一大賣點,即使有多個伺服器,這點仍然不變。伺服器上的一舉一動,會立即廣播至其他伺服器。全部伺服器都是同步的。」

「以多個伺服器建立一個虛擬世界,對伺服器間的連線速度和延遲要求想必十分嚴格。能否講一下你們如何達到這些要求?」「這個嘛…」與其他職員討論一番後:「…我們也不是太清楚,因為這部份的網絡是由智匯總部負責。我們所知的只是伺服器間是以某形式的量子纏繞連接,基本上等同有專線連接,所以毋須擔心遊戲會加重全球網絡的負擔噢。」

「你們是智匯集團的新成員。我想問的是,如果沒有集團的資助,你們能完成遊戲嗎?」「嗯……不可能。」「即使靠籌款得到更多資金也不能?」

「尤其有更多資金時,更加不可能。…」年紀跟亞莎相約的男生回應。「…籌款的問題在於,資金量難以預計。…」畫面顯示他是長春藤創辦人之一,里奥。「…如果資金忽然增多,而且遠多於原預算,製作組就好容易誤用資源。你說,過去十年有多少遊戲商死於這手上?我想,如果我們籌款模式的話,我們的命運也不會例外。而且,智匯網絡給予我們的,不只是資助。先不要說遊戲的骨幹網絡是由智匯架設,就連這辦公室的設計和我們的工作流程,智匯也提過很多意見。雖然你們已屢次從其他製作組聽過,但還請容我再說一遍:若是沒有智匯網絡的協助,就不會有紀道域,長春藤也可能早已解散。」

一位記者突然笑起來。「怎麼了?這裏地方有哪裏可笑嗎?還是我們說錯什麼?」亞莎緊張地問。「都不是。這問題從一個串流頻道而來,估計原本由其觀眾提出。…」還是忍不住笑:「…我就照讀出來了:『亞莎你可以當我的女朋友嗎?』」

眾人終於理解記者笑的原因,共用空間旋即被笑聲掩蓋。亞莎則呆了數秒,然後:「不行啊。…」一手抱住里奥的手臂,「…我已有這傢伙。」拋出長春藤第一個公眾形像災難。

擾攘一輪,時間將近零時。共用空間的其中一面牆變成屏幕,顯示不屬於這個世界的畫面。現場和觀看直播者都屏息靜待;人人皆知道,開張儀式即將開始。

屏幕分作五個畫面,顯示五個超大型城市,每個都容得下百萬人有餘;紀道域究竟有多大,實在難以想像。「這些地點,測試玩家應該認得,是玩家首次登入時進入的地方。用以前的術語來說的話,是新手村,雖然『一個世界』令其遠遠脫離『村』的規模。」鏡頭轉向城內的中央廣場。

屏幕上時鐘,表示距離遊戲正式開張還有……「十﹑九,…」亞莎帶頭跟全世界一起倒數:「…八﹑七…」本納覺得倒數聲有如親耳聽到般震撼,「…六…」當他意識到原來他附近的人都在一起倒數時,也加入倒數:「…五﹑四﹑三,二﹑一,零!」踏入二零二八年八月一日,紀道域的伺服器正式投入運作。

……鴉雀無聲。城市沒有動靜,現場無人作聲。「什麼也沒有。…」人們除了感到奇怪外,亦慶幸自己沒有拍手或叫喊。「…大家想這樣問吧?這是當然的:建立角色需時,對外觀如何隨便的人也至少要一分鐘吧?」

二十秒後,中央廣場上浮現絲帶狀警告。在警告的光茫下,遊戲的首名玩家現身。「啪」的一聲,廣場與長春藤工作室同時滿佈彩帶。「歡迎來到紀道域!」歡呼聲此起彼落。

更多玩家現身。一個、兩個,越來越多玩家在對鏡頭揮手。運作中的鏡頭都沒有立體模型;他們之所以會知道鏡頭所在,乃因為有人在遊戲內打開瀏覽器觀看直播。

製作三年,當中辛苦只有製作人員知曉。三年耕耘終成果;看見遊戲化為成功,猶如看見子女成才。從兩個世界包圍製作人員的掌聲、歡呼聲、與揮手,都充滿道謝之意。製作人員再也按耐不住,感動得落涙。

下一章

Monday 3 June 2013

MMD: 0

漆黑的空間中,唯一看得見的是一個男子。就好像他是這個空間中唯一的物質。又或者說,從他身上不自然光影看來,就好像他是空間中唯一的光源。

一眨眼,男子前方出現黑暗中另一可見之物。紅色標語懸浮空中,寫有警告字句。帶狀的警告頭尾相連,成一圓圈,在空中自轉。更多標語憑空出現,全都在繞某個「中心」自轉。

中心離男子約二十米。這一刻那裡空無一物,下一刻就有個木箱。木箱因為警告的紅光而染成櫻桃色。箱子在跌下來,警告消失後,才變回原有的顏色。

再有警告出現在木箱之上。警告和木箱相繼出現的循環重複數十次,堆成一座木箱山。此刻男子已在急步後退。

靜下不久,又有警告。這次警告的中心並不是空白,而是一個木箱。新的木箱出現後,兩個木箱以物理上不可能的方式重疊,並

猛烈震動。兩箱子承受不了這超乎常理的異力,雙雙爆炸。

男子還沒來得及反應,又再有一對箱爆炸。幾秒內,整個木箱山都變成木屑煙火。

「喂!可歡,你想殺了我嗎?」男人跌倒在地上后說。「對不起噢。不過這個提議值得考慮一下。」聞其聲不見其影的女子說。

新一輪的爆炸又再開始,而且久久未有停下的徵兆。剩下的木屑堆積成山。眼見狀況,男子就想逃離。

念頭萌生之時,一塊平面顯示在他面前。上面寫有:「正在退出」,下方的數字由十開始倒數。

數字歸零,空間中唯一的人消失於漆黑之中。

男子張開眼,離開那黑暗的夢境。醒來後,他首先察覺的是頭被什麼匝着。他摘掉那戴着就只會露出臉的頭盔,在床上坐起身。

「似乎問題不在物理引擎本身。這似乎也是死胡同,你打算試到何時?」他對眼前的女生說。她正在盯着終端機,一副專注的樣子:「直到再出現為止。」是剛才聽到的那把聲線,但這次多了一份氣餒。「被我們犧牲的木箱大概上千了吧。」「算上剛才的,一千八百一十二個。」「即是你剛才一口氣炸掉上千個箱嗎……」

記錄檔上最新的一行寫着「玩家『冀思』已登出」。「說起來…」突然一臉不滿地望向男子:「…為什麼你要登出得這麼快?本來我可打算運行夠十分鐘的!」

「你還好意思說,『鮮花』可是連痛覺也會模擬的。你將爆炸場境設在我正前方,你可有理過我感受?」「可是,爆炸如此激烈,你不靠近的話,不就什麼也觀察不了?」

「為什麼一定要用人觀測?鮮花本身就有除錯用的記錄工具,而且你不是還有放置鏡頭去錄影嗎?那麼少我一個也沒所謂吧。」

「你明知鮮花本身記錄不會有用。」「就算是沒有用,至少也要先找到重現條件才部署觀察人員吧。」「既然記錄上不會顯示,那我怎知道何時重現了那錯誤?」

「你有沒有看過那次的記錄?」「只看過錄像,沒看過數據。畢竟那不是我的工作範圍。」「難怪。可悲的是,今次我所知的不比你這懶人多。」「為什麼?」「你自己看。」

終端機幾乎等身高的螢幕上跑出幾個畫面,都是小鎮街景。冀思認得這地方:那個錯誤第一次出現﹑亦是最後次出現的地方。當時美術人員正在佈置場景,四處頻繁出現剛才見過的警告。

在市場中的約翰,本來負責概念圖。他見工作半年前就已完成,閒着沒事就自願接上建構地圖的工作。

他之所以會自動請纓,除了因為空閒,還有別的原因:他想感受一下這個世界,感受這個他有份創造的世界。

蔚藍的天空﹑容積雲投下的影子﹑地上水坑刺眼的反光﹑貨攤上精緻的食材模型。一切都跟概念圖上的一樣。自動調節的物料設定,令物件的細節比概念圖更出色。

遊戲引擎「鮮花」能帶給玩家的,不只這些。微風吹過,香草與蘑菇的香味撲鼻而來。「還好是在乾貨區。我可不想對『鮮花』的第一印象是魚腥跟生肉味。」

張開懸空的藍圖,約翰繼續建設市場。或者說是「擺設」比較貼切吧?畢竟,這正是約翰在做的:按照藍圖,將一件件預先製備的模型放到這個世界。

本來,開發者能透過鮮花的「傳感」介面,從腦部直接給予指令,不靠選單﹑不動手腳就能移動物件。不過約翰平常繪畫平面的概念圖,不太習慣三維控制,所以仔細的調節他都會親手做。

木盤的質感與木紋﹑貨物在盤上滾動而做成的重心轉移﹑貨物間的碰撞聲﹑手臂因搬運重物而傳來的酸軟感。很難相信這一切也是遊戲引擎實時模擬出來。

要說傳感介面的壞處,就是使用者一分神就很容易出錯。「糟糕。」連續下了兩個放置指令,將兩個木箱放在相同位置。

警告標語包圍首先出現的木箱,然後……什麼也沒發生。

沒有爆炸, 沒有猛烈搖晃,沒有碰撞聲,第二個木箱甚至沒有出現。只有首先出現的木箱,若無其事地站在地上。一切也靜得異常,直至東邊傳來尖叫聲。

眾人跑到離市場四個街口的現場。在那裏,一位女工作人員坐在地上,又驚訝又疑惑地望着前方;在她面前,是約翰所放置的木箱。

可歡停暫停錄像,回帶到約翰放置木箱的一刻,放到螢幕一角,然後打開記錄檔。看到這密密麻麻﹑頁復一頁的文字,冀思通常都會立即感到頭昏腦脹。不過,這次沒有。一來,可歡只篩選相關的資料顯示;二來,就算是篩選過,這次記錄檔上的項目都明顯比平常少。

「終於了解吧。這就是一九零九六奇異之處。」開發者模式顯示兩個木箱確是被召喚至同一位置,可是物理引擎蒲公英卻沒有木箱轉移位置前的記錄,就像木箱由一開始已在市場外。

「根據這些系統的記錄,整件事豈不跟沒有發生過一樣嗎?」「不然你以為我是出於什麼原因才每次測試都需要你在?」「原來如此。不過這樣種錯誤,到底要怎樣才能找出來?」「基本上同平時無分別,惟不能依靠系統信息與記錄而已。」

可歡除下白色幼身頭箍。像是了解到可歡要離開一樣,視窗一個個關閉。取而代之的,是螢幕正中的二頭身人像。人像很像可歡,甚至穿著同樣的羊毛外套。估計是可歡在工餘時畫的。

「反正你已經出來,不如先吃些什麼吧。」可歡站起來伸個懶腰。身後本來的白色牆壁漸漸變得透明,染上街外茜色。夕陽透過落地玻璃窗照到室內。看着黃昏美景,「我真的很喜歡這辦公室。」可歡不禁從心底嘆道。

在離開辦公室的路上,「你有信心在正式發佈前確認到這個錯誤嗎?」一九零九六號錯誤,兩個月前首次出現,迄今未有人能重現。所以,其在錯誤資料庫中,只是「未確認」的「事件」。「老實說,就算能把全部時間都放到一九零九六上,也沒有把握。你不忘了頭一周有多少測試員在嘗試重現嗎?」

「印象中,好像沒有誰沒有試過……」當時就連非測試部的員工都加入測試行列,希望首先確定那個奇觀。其實,這本身就已是個奇觀。「不就是。現在就只剩下我們這組仍持有信心,不,是抱有懷疑,是什麼都好,總之現在只剩我們。人力物力到底是那時的幾分之一?單是想起也覺得頭痛。」不過,他們大多沒有正確的程序與適合的工具,就算他們真的能重現事件,最多也只能當作多個樣本作參考。

「我們已經專注在這錯誤大半星期,上司不會容我們再放太多時間吧。如果今日內確認不了的話,就只能先放下它了。唉,這幾天到底堆了多少新的劇本?本來以為只有編程部的人才是這樣,臨到期限就會推來一大堆代碼。該說這果然是人的天性嗎……」

「喂,可歡﹑冀思!」食堂內,是測試部的另一隊伍,馬克與尚樂。「論偷懶,還是你們更勝一籌。」可歡坐在馬克旁。「你這樣說有損我們名聲啊。我們過來前已完成工作目標。」「你們又怎樣?」尚樂接上話題,問二人。「不要提。堆得跟山一樣高。」冀思道, 並向可歡投以責怪的眼神。

「還在執着於那個嗎?」「不行嗎?」「你們花了這麼長的時間都再見不到一九零九六,也許就算遊戲正式推出後,也永遠不會再見到吧。」

「不要天真。梅非定律指出…」「凡會出錯的,都會出錯嘛。」眾人異口同聲說。「…你們知道就好。不要忘記『紀道域』的首月目標銷售量是二百萬。就算每人導致出錯機會只有一百萬分之一,這二百萬人都不會觸發這錯誤的機率是……」可歡數起手指來。她到底想如何靠手指算出二百萬次方,實在不得而知。

「百分之十三點五。」馬克的「眼鏡」已算出並顯示答案。「謝謝。這統計模型雖然是十分簡化,但亦只計算頭一個月的玩家。那你們該知道我們對大型遊戲有多重要吧?」

馬克無視可歡的偉論:「為什麼你不用『眼鏡』?」逕自提問。「基於工作需要,我已經要戴半天傳感裝置。若連工餘時間都戴着它,我怕會分不清現實與虛擬。」席上,唯可歡頭上沒戴傳感裝置和眼鏡。

「你擔心的時候,有考慮過我們的感受嗎?」尚樂抗議。他與冀思一樣,是有「潛水員」之稱的測試員。所謂「潛水員」,需要「潛行」至遊戲中,從內部測試。他們不但會經傳感裝置下達指令予輸入模組,還會由輸出模組將虛擬官感直接讀入腦中。雖然兩種技術都已有上十年的臨床經驗,但要說潛水員的認知所面對的危險性的話,確是比身為「操作員」的可歡高。

再者,近年很多受歡迎的故事都以「被困潛行狀態」作主題。除了因為虛擬世界的設定容易吸引人外,亦因為理論上有此可能。傳感頭盔啟動後,會偵測大腦運動神經元移動肌肉的訊號,作為內部運算之用,亦會發射中和訊號,以防止潛行時身體有過多活動。雖然小說多以昏迷來比擬潛行狀態,但潛行的人其實還是能夠感受外界刺激和作出反應,只是骨骼肌活動受到限制;要比的話,是以路格瑞氏症的症狀較為貼切,

每當鮮花加入新功能,只要通過自動測試與檢查,就會交由三支先頭隊伍測試,尚樂與馬克就是其中一隊。雖然現實從未發生事故,但尚樂始終站在潛行的應用層面最前端,要擔心亦不無道理。

「抱歉。但我始終對自己的意志力沒有信心。」「我沒有責備之意,僅是想說你杞人憂天。」站在前端的好處,在於工作來得早亦去得早。馬克和尚樂現在之所以空閒,除了因為他們工作快,亦因為遊戲的引擎部份早已穩定,改動不多。

可歡所屬的隊伍,以測試劇本和介面為主;在遊戲推出前,工作只會越來越多。多人遊戲的存活依賴不停引入優質的新內容,所以遊戲推出後的工作量亦不會減少。

距離「紀道域」推出之時,尚餘兩個月。

下一章

Tuesday 28 May 2013

JavaScript Constant

As a good programmer, you make everything constant. By doing so, you limit the places that you can make mistakes, and thus reduce number of bugs... at least works most of the time.

I wrote an web page for an assignment. Naturally, I used the const keyword all over the place. I did not even think about the possibility that JavaScript has no constant as VIM highlights the keyword, and the page worked perfectly on Firefox and Opera on my machine.

Therefore, I was very surprised when I found the page bugged out on the tutor's computer with Google Chrome.

The story starts with scoping rule of JavaScript. Variables in JavaScript has either global scope or function scope. Nested functions can access variables of the nesting function. This is called scope chain. Unlike most languages, JavaScript does not have a block scope; the following is valid JavaScript code

function a() {
    if (true) {
        var b = 2;
    }
    alert(b);
}

What about constants? What will happen in this piece of code?

function c() {
    for (var i = 0; i < 4; i++) {
        const j = i;
        alert(j);
    }
}

ECMAScript 5, the base language of the current version of JavaScript, has no constants. Constant is a proposed feature of the next version of ECMAScript, Harmony. There is also block-scoped variable in Harmony, declared using the keyword let.

Firefox 20 treats constant as a block scope variable; Opera 12 does not distinguish between constants and variables (you can reassign a "constant"). My code works in both cases. In Chrome / Chromium 26, however, constants have function scope. You can declare a constant in a loop, the constant will not update its value at all. The above code will alert 0 four times. What makes this worse is Chrome / Chromium will not complain the failure of assignment.

ECMAScript 6, can you standardize a bit faster?

Sunday 14 October 2012

Trouble-shooting: Seg-fault (erroneous memory access) on glDrawElements()

Yesterday, when I was writing basic rendering code for the new ViRe, I keep getting errors of erroneous memory access (in the form of an Ada exception).

With the small code size I currently have, finding the offending line of source code is a matter of minutes. However, knowing that the exception is raised during the call to glDrawElements(), I still don't know the cause of it. My first suspect is the Ada OpenGL binding that I have been writing alongside the renderer.

Today, I wrote a small programme (~100 lines of Ada source text), set up everything that is needed and called glDrawElements(). To my surprise, it worked perfectly. The binding is working fine; it is the code that loads and prepares data that is in fault.

After some time, I found out that if you call glDrawElements on a VAO that is not ready to be rendered, be it a non-existent array object (including the id 0) or an uninitialised one, a segmentation fault will happen. By uninitialised, I mean the vertex buffer or index buffer is not generated, bind, or you forgot to load data into the index buffer, or you failed to enable the corresponding vertex attribute array, or any combination of them.

As long as index data is present, its values does not matter. It may cause an empty screen but not a memory access violation. Absence of vertex data does not affect rendering process.

Note that glDrawArrays() does not generate this error regardless of the circumstances. That makes sense, since it does not rely on index buffer object or index data.

Development environment:

  • OS: Linux Mint 13 with kernel 3.2.0-30-generic
  • Display Card : NVIDIA GT640M LE
  • Display Card driver: Bumblebee with NVIDIA binary driver 3.0.4.51