25年前,開發者如何將遊戲塞進內存?

原文:How did game developers pack entire games into so little memory twenty five years ago?
譯者:傑微刊--張帆 算法


25年前,開發者是如何將遊戲塞進那麼小的內存中的? dom

Quora上,這個問題得到了50萬人的閱覽,Dave Baggett對問題的回答也得到了六千多的點贊,其中不乏遊戲大師。 ide

 

傑微刊:25年前,開發者如何將遊戲塞進內存?

Crash Bandicoot遊戲封面 函數

 

問題描述 工具

 

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


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


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


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


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字節。那真是美好時光啊~

 

 


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

若是您發現這篇譯文的任何問題,可隨時與傑微刊聯繫。

咱們水平有限,但理想高遠。傑微刊旨在分享優質的內容。

[轉載請保留原文出處、譯者和審校者。 能夠不保留咱們的連接]

相關文章
相關標籤/搜索