25年前,開發(fā)者如何將游戲塞進(jìn)內(nèi)存?
Crash Bandicoot 游戲封面
英文原文:How did game developers pack entire games into so little memory twenty five years ago?
25 年前,開發(fā)者是如何將游戲塞進(jìn)那么小的內(nèi)存中的?Quora 上,這個(gè)問題獲得了 50 萬(wàn)人的閱覽,Dave Baggett 對(duì)問題的回答也獲得了六千多的點(diǎn)贊,其中不乏游戲大師。
問題描述
家庭游戲系統(tǒng)軟件采用了 64K~128K 的磁卡(cartridge),然而卻能夠提供玩好幾個(gè)小時(shí)的各式各樣的圖形、精靈鬼怪和聲音。游戲系統(tǒng)好像要提供大量的功能(功能性的函數(shù)、庫(kù)、硬件加 速和圖形指令等等)似的,大量的圖片、音樂和音效、動(dòng)畫效果、游戲算法能放入如此小的存儲(chǔ)空間中,是多么得令人吃驚,更別提是 25 年前!
上面提到的存儲(chǔ)空間大小目前看來也就等同于一個(gè)采用中等分辨率壓縮(moderate-resolution compressed)的 JPEG 文件——一張圖片而已。我十分好奇,在那個(gè)年代軟件開發(fā)究竟是怎么一回事。我堅(jiān)信在當(dāng)時(shí),開發(fā)者肯定沒有一個(gè)所謂編寫開源軟件的協(xié)作開發(fā)環(huán)境,更別提在那 樣一個(gè)軟件開發(fā)能收獲巨大經(jīng)濟(jì)回報(bào)的時(shí)代。
我十分好奇,那個(gè)年代的開發(fā)者究竟基于哪些知識(shí)、技術(shù)、想法或者洞察力完成了這樣的開發(fā)。有沒有可能一些想法已經(jīng)丟失或者沒有被記錄下來?曾經(jīng) 的電子游戲種類如此豐富,并且使數(shù)以百萬(wàn)的人花費(fèi)數(shù)百小時(shí)在上面獲得快樂,更別提游戲開發(fā)采用了這樣高效的方式——這顯然是一項(xiàng)壯舉。這種效率讓我想起了 錄制音樂 demo。結(jié)合這些,我想提出下列的問題:如何更好理解計(jì)算機(jī)科學(xué)的原則和技術(shù)的使用方式,比如,如何將 64K 的 demo 進(jìn)行編碼。
這個(gè)問題的重點(diǎn)是那個(gè)時(shí)候的專業(yè)技能:那時(shí)的開發(fā)者為何如此成功?他們使用了哪些已經(jīng)失傳的技術(shù)、解決辦法,或者算法技巧?
Dave Baggett 的回答
下面是 20 世紀(jì) 90 年代末的一個(gè)相關(guān)軼事。當(dāng)時(shí),我和 Andy Gavin 共同為 PS1 編寫游戲“古惑狼”(Crash Bandicoot)。
在當(dāng)時(shí),RAM 依然是最主要的問題。PS1 只有 2MB 的 RAM,然而我們不得不做一些瘋狂的事情使游戲適配硬件。我們的游戲數(shù)據(jù)大約有 10MB 大小,所以我們不得不進(jìn)行動(dòng)態(tài)的分頁(yè)輸入輸出(paged in and out dynamically),雖然加載滯后幀速率就會(huì)下降到 30Hz,但是我們并沒有任何故障。
之所以能成功運(yùn)行,是因?yàn)?Andy 寫了一個(gè)令人難以置信的分頁(yè)系統(tǒng),可以將 64K 數(shù)據(jù)頁(yè)進(jìn)行交換,從而作為古惑狼這款游戲的數(shù)據(jù)遍歷水平。這就是當(dāng)時(shí)的 “全棧”能力,在這個(gè)分頁(yè)系統(tǒng)中,運(yùn)行了上至高級(jí)內(nèi)存管理,下至操作碼級(jí)別的直接內(nèi)存訪問系統(tǒng)(DMA)的全部代碼。Andy 甚至控制了 CD-ROM 磁盤上字節(jié)的物理布局,這樣一來,即使磁盤的速率是 300KB/s,PS1 還是能在游戲執(zhí)行到某個(gè)位置時(shí)加載到相應(yīng)的數(shù)據(jù)。
我當(dāng)時(shí)主要負(fù)責(zé)編寫打包工具,這個(gè)工具的功能是將資源文件,比如聲音、圖形圖像、小動(dòng)物的聲音控制代碼(譯者注:lisp control code)等打包為 64K 的數(shù)據(jù)分頁(yè),塞進(jìn) Andy 的系統(tǒng)當(dāng)中去。(順帶一提,這個(gè)問題——將任意大小的對(duì)象打包成固定大小的數(shù)據(jù)分頁(yè),生產(chǎn)數(shù)據(jù)包——是一個(gè) NP 完全問題,所以看起來在合理的時(shí)間或者線性復(fù)雜度下得到***解是不可能的。)
然而有些時(shí)候算法并不合適,我的打包工具采用了各種各樣的算法(如***適配、***適配(first-fit,best-fit)等等),只為找 到***的打包方案,包括使用隨機(jī)搜索類似于 Simulated annealing 中用到的梯度下降的過程?;旧?,我寫了一大堆不同的打包策略,一一嘗試,并擇優(yōu)選擇。
像那樣使用隨機(jī)指導(dǎo)搜索(a random guided search)的問題在于,你永遠(yuǎn)不知道你能否再次得到同樣的結(jié)果。有些古惑狼的關(guān)卡只能靠“碰碰運(yùn)氣”來進(jìn)行隨機(jī)打包,并放入***允許頁(yè)數(shù)(我印象中是 21)。這意味著,一旦你將這個(gè)關(guān)卡打包,可能更改了一個(gè)烏龜?shù)拇a,就永遠(yuǎn)不會(huì)再找到這個(gè) 21-分頁(yè)的數(shù)據(jù)包了。有幾次,一個(gè)藝術(shù)家修改一些內(nèi)容,就會(huì)毀掉現(xiàn)行的分頁(yè)計(jì)數(shù),所以我們不得不半隨機(jī)似的改變其他內(nèi)容,直到打包器能再次找到可用的數(shù) 據(jù)包。并且我還要在凌晨 3 點(diǎn)給這個(gè)倔強(qiáng)的藝術(shù)家解釋清楚。
現(xiàn)在回憶起來,到目前為止***的部分,也是當(dāng)時(shí)最糟糕的部分,正是使核心C/程序集代碼(C/assembly code)適配。那時(shí)距離“最終測(cè)試版”的發(fā)版期限沒有幾天了,而這幾天是我們抓住假期發(fā)行游戲的***機(jī)會(huì),在此之前,我們失去了整整一年的時(shí)間。當(dāng)時(shí)我 們正在將語(yǔ)義上相同,而語(yǔ)法上具有不同表現(xiàn)(semantically identical but syntactically different manifestations)的C語(yǔ)言代碼進(jìn)行隨機(jī)排列(permute),以此希望編譯器能夠生成 200 字節(jié)、125 字節(jié)、50 字節(jié)然后是小于 8 字節(jié)的代碼。
作為當(dāng)時(shí)排列所采用的方法“for (i=0; i < x; i++)”——如果我們采用上面用到的變量,并使用 while 循環(huán)來重寫這段方法,以用作他用,那么會(huì)發(fā)生什么呢?這是我們嘗盡各種一般方法——比如像將數(shù)據(jù)塞進(jìn)指針的***兩位(這個(gè)方法只能在 R3000 上生效,因?yàn)樗械牡刂范际?4 字節(jié)對(duì)齊的(4-byte aligned))——之后的解決之道。
最終,古惑狼成功了的適配了 PS1 的內(nèi)容,還多出了 4 字節(jié)的空閑空間。是的,2097152 之外的 4 字節(jié)。那真是美好時(shí)光啊。
---------------------------------------------------------
如果您發(fā)現(xiàn)這篇譯文的任何問題,可隨時(shí)與杰微刊聯(lián)系。我們水平有限,但理想高遠(yuǎn)。杰微刊旨在分享優(yōu)質(zhì)的內(nèi)容。杰微刊也同樣期待理想的您對(duì)這個(gè)世界的貢獻(xiàn)。歡迎任何目的的聯(lián)系。杰微刊的有償投稿郵箱是:weikan@jointforce.com。我們的 QQ 是:3272840549。