25年前,開發者如何將遊戲塞進記憶體?

發表於2019-05-11
原文:How did game developers pack entire games into so little memory twenty five years ago?
譯者:傑微刊--張帆

25年前,開發者是如何將遊戲塞進那麼小的記憶體中的?
Quora上,這個問題獲得了50萬人的閱覽,Dave Baggett對問題的回答也獲得了六千多的點贊,其中不乏遊戲大師。

[img=遊戲,記憶體,500,259]https://jf-bucket-public.oss-cn-qingdao.aliyuncs.com/jfperiodical/attached/image/20150630/747164897.png[/img]

Crash Bandicoot遊戲封面



問題描述 

家庭遊戲系統軟體採用了64K~128K的磁卡(cartridge),然而卻能夠提供玩好幾個小時的各式各樣的圖形、精靈鬼怪和聲音。遊戲系統好像要提供大量的功能(功能性的函式、庫、硬體加速和圖形指令等等)似的,大量的圖片、音樂和音效、動畫效果、遊戲演算法能放入如此小的儲存空間中,是多麼得令人吃驚,更別提是25年前!

上面提到的儲存空間大小目前看來也就等同於一個採用中等解析度壓縮(moderate-resolution compressed)的JPEG檔案——一張圖片而已。我十分好奇,在那個年代軟體開發究竟是怎麼一回事。我堅信在當時,開發者肯定沒有一個所謂編寫開源軟體的協作開發環境,更別提在那樣一個軟體開發能收穫巨大經濟回報的時代。

我十分好奇,那個年代的開發者究竟基於哪些知識、技術、想法或者洞察力完成了這樣的開發。有沒有可能一些想法已經丟失或者沒有被記錄下來?曾經的電子遊戲種類如此豐富,並且使數以百萬的人花費數百小時在上面獲得快樂,更別提遊戲開發採用了這樣高效的方式——這顯然是一項壯舉。這種效率讓我想起了錄製音樂demo。結合這些,我想提出下列的問題:如何更好理解電腦科學的原則和技術的使用方式,比如,如何將64K的demo進行編碼。

這個問題的重點是那個時候的專業技能:那時的開發者為何如此成功?他們使用了哪些已經失傳的技術、解決辦法,或者演算法技巧?

Dave Baggett的回答 

下面是20世紀90年代末的一個相關軼事。當時,我和Andy Gavin共同為PS1編寫遊戲“古惑狼”(Crash Bandicoot)。

在當時,RAM依然是最主要的問題。PS1只有2MB的RAM,然而我們不得不做一些瘋狂的事情使遊戲適配硬體。我們的遊戲資料大約有10MB大小,所以我們不得不進行動態的分頁輸入輸出(paged in and out dynamically),雖然載入滯後幀速率就會下降到30Hz,但是我們並沒有任何故障。

之所以能成功執行,是因為Andy寫了一個令人難以置信的分頁系統,可以將64K資料頁進行交換,從而作為古惑狼這款遊戲的資料遍歷水平。這就是當時的 “全棧”能力,在這個分頁系統中,執行了上至高階記憶體管理,下至操作碼級別的直接記憶體訪問系統(DMA)的全部程式碼。Andy甚至控制了CD-ROM磁碟上位元組的物理佈局,這樣一來,即使磁碟的速率是300KB/s,PS1還是能在遊戲執行到某個位置時載入到相應的資料。

我當時主要負責編寫打包工具,這個工具的功能是將資原始檔,比如聲音、圖形影像、小動物的聲音控制程式碼(譯者注:lisp control code)等打包為64K的資料分頁,塞進Andy的系統當中去。(順帶一提,這個問題——將任意大小的物件打包成固定大小的資料分頁,生產資料包——是一個NP完全問題,所以看起來在合理的時間或者線性複雜度下得到最優解是不可能的。)

然而有些時候演算法並不合適,我的打包工具採用了各種各樣的演算法(如最先適配、最好適配(first-fit,best-fit)等等),只為找到最好的打包方案,包括使用隨機搜尋類似於Simulated annealing中用到的梯度下降的過程。基本上,我寫了一大堆不同的打包策略,一一嘗試,並擇優選擇。

像那樣使用隨機指導搜尋(a random guided search)的問題在於,你永遠不知道你能否再次得到同樣的結果。有些古惑狼的關卡只能靠“碰碰運氣”來進行隨機打包,並放入最大允許頁數(我印象中是21)。這意味著,一旦你將這個關卡打包,可能更改了一個烏龜的程式碼,就永遠不會再找到這個21-分頁的資料包了。有幾次,一個美術設計修改一些內容,就會毀掉現行的分頁計數,所以我們不得不半隨機似的改變其他內容,直到打包器能再次找到可用的資料包。並且我還要在凌晨3點給這個倔強的藝術家解釋清楚。

現在回憶起來,到目前為止最好的部分,也是當時最糟糕的部分,正是使核心C/彙編程式碼(C/assembly code)適配。那時距離“最終測試版”的發版期限沒有幾天了,而這幾天是我們抓住假期發行遊戲的最好機會,在此之前,我們失去了整整一年的時間。當時我們正在將語義上相同,而語法上具有不同表現(semantically identical but syntactically different manifestations)的C語言程式碼進行隨機排列(permute),以此希望編譯器能夠生成200位元組、125位元組、50位元組然後是小於8位元組的程式碼。

作為當時排列所採用的方法“for (i=0; i < x; i++)”——如果我們採用上面用到的變數,並使用while迴圈來重寫這段方法,以用作他用,那麼會發生什麼呢?這是我們嚐盡各種一般方法——比如像將資料塞進指標的最低兩位(這個方法只能在R3000上生效,因為所有的地址都是4位元組對齊的(4-byte aligned))——之後的解決之道。

最終,古惑狼成功了的適配了PS1的內容,還多出了4位元組的空閒空間。是的,2097152之外的4位元組。那真是美好時光啊~

-------------------好久不見的分割線-------------------

如果您發現這篇譯文的任何問題,可隨時與傑微刊聯絡。
我們水平有限,但理想高遠。傑微刊旨在分享優質的內容。

傑微刊也同樣期待理想的您對這個世界的貢獻。歡迎任何目的的聯絡。
傑微刊的有償投稿郵箱是:weikan@jointforce.com
我們的QQ是:3272840549。

[轉載請保留原文出處、譯者和審校者。 可以不保留我們的連結]
原文點此檢視
回覆

相關文章