發(fā)布時(shí)間:2023-11-27 13:12:02 瀏覽量:127次
幾個(gè)月前,JS1k游戲制作節(jié)(JS1K game jam)傳出不再舉辦消息后,許多游戲迷開(kāi)始哀嚎。
Frank Force 也是其中一位,但他還有另一層身份——一位德克薩斯州奧斯汀的獨(dú)立游戲設(shè)計(jì)師。Frank Force 在游戲行業(yè)工作了20年,參與過(guò)9款主流游戲、47個(gè)獨(dú)立游戲的設(shè)計(jì)。在聽(tīng)到這個(gè)消息后,他馬上和其他開(kāi)發(fā)朋友討論了這個(gè)問(wèn)題,并決定做點(diǎn)什么為此紀(jì)念。
在此期間,他們受到三重因素的啟發(fā)。一是賽車(chē)游戲,包括懷舊向的80年代賽車(chē)游戲,他們?cè)诜浅T缙诘挠布贤苿?dòng)實(shí)時(shí) 3D 圖形,所以作者沿用了相同的技術(shù),用純 JavaScript 從頭開(kāi)始實(shí)現(xiàn)做 3D 圖形和物理引擎;還有一些現(xiàn)代賽車(chē)游戲帶來(lái)了視覺(jué)設(shè)計(jì)的靈感,比如《Distance》和《Lonely Mountains: Downhill》;二是之前 Jake Gordon 用 JavaScript 創(chuàng)建一個(gè)虛擬3D賽車(chē)的項(xiàng)目,并分享了代碼;三是 Chris Glover 曾經(jīng)做過(guò)一款小到只有 1KB 的 JS1k 賽車(chē)游戲《Moto1kross by Chris Glover》。
于是 Frank 和他的朋友們決定做一個(gè)壓縮后只有 2KB 的 3D 賽車(chē)游戲。2KB 到底有多小呢?提供一個(gè)參考,一個(gè)3.5英寸軟盤(pán)可以容納700多個(gè)這樣的游戲。
他給這個(gè)游戲取名 Hue Jumper。關(guān)于名字的由來(lái),F(xiàn)rank 表示,游戲的核心操作是移動(dòng)。當(dāng)玩家通過(guò)一個(gè)關(guān)卡時(shí),游戲世界就會(huì)換一個(gè)顏色色調(diào)?!霸谖蚁胂笾?,每通過(guò)過(guò)一個(gè)關(guān)卡,玩家都會(huì)跳轉(zhuǎn)到另一個(gè)維度,有著完全不同的色調(diào)?!?/p>
做完這個(gè)游戲后,F(xiàn)rank 將包含了游戲的全部 JavaScript 代碼都發(fā)布在他的個(gè)人博客上,其中用到的軟件主要也是免費(fèi)或開(kāi)源軟件的。游戲代碼發(fā)布在CodePen,可以在 iframe 中試玩,有興趣的朋友可以去看看。
以下是原博內(nèi)容,AI源創(chuàng)評(píng)論進(jìn)行了不改變?cè)獾木幾g:
因?yàn)閲?yán)格的大小限制,我需要非常仔細(xì)對(duì)待我的程序。我的總體策略是盡可能保持一切簡(jiǎn)單,為最終目標(biāo)服務(wù)。
為了幫助壓縮代碼,我使用了 Google Closure Compiler,它刪除了所有空格,將變量重命名為1個(gè)字母字符,并進(jìn)行了一些輕量級(jí)優(yōu)化。
用戶可以通過(guò) Google Closure Compiler 官網(wǎng)在線跑代碼。不幸的是,Closure Compiler 做了一些沒(méi)有幫助的事情,比如替換模板字符串、默認(rèn)參數(shù)和其他幫助節(jié)省空間的ES6特性。所以我需要手動(dòng)撤銷(xiāo)其中一些事情,并執(zhí)行一些更“危險(xiǎn)”的壓縮技術(shù)來(lái)擠出最后一個(gè)字節(jié)空間。在壓縮方面,這不算很成功,大部分?jǐn)D出的空間來(lái)自代碼本身的結(jié)構(gòu)優(yōu)化。
代碼需要壓縮到2KB。如果不是非要這么做不可,有一個(gè)類(lèi)似的但功能沒(méi)那么強(qiáng)的工具叫做 RegPack 。
無(wú)論哪種方式,策略都是一樣的:盡最大可能重復(fù)代碼,然后用壓縮工具壓縮。最好的例子是 c.width,c.height和 Math。因此,在閱讀這段代碼時(shí),請(qǐng)記住,你經(jīng)常會(huì)看到我不斷重復(fù)一些東西,最終目的就是為了壓縮。
其實(shí)我的游戲很少使用 html ,因?yàn)樗饕玫降氖?JavaScript 。但這是創(chuàng)建全屏畫(huà)布 Canvas ,也能將畫(huà)布 Canvas 設(shè)為窗口內(nèi)部大小的代碼最小方法。我不知道為什么在 CodePen 上有必要添加 overflow:hiddento the body,當(dāng)直接打開(kāi)時(shí)按理說(shuō)也可以運(yùn)行。
我將 JavaScript 封裝在一個(gè) onload 調(diào)用,得到了一個(gè)更小的最終版本… 但是,在開(kāi)發(fā)過(guò)程中,我不喜歡用這個(gè)壓縮設(shè)置,因?yàn)榇a存儲(chǔ)在一個(gè)字符串中,所以編輯器不能正確地高亮顯示語(yǔ)法。
有許多常量在各方面控制著游戲。當(dāng)代碼被 Google Closure 這樣的工具縮小時(shí),這些常量將被替換,就像 C++ 中的 #define 一樣,把它們放在第一位會(huì)加快游戲微調(diào)的過(guò)程。
鼠標(biāo)是唯一的輸入系統(tǒng)。通過(guò)這段代碼,我們可以跟蹤鼠標(biāo)點(diǎn)擊和光標(biāo)位置,位置顯示為-1到1之間的值。
雙擊是通過(guò) mouseUpFrames 實(shí)現(xiàn)的。mousePressed 變量只在玩家第一次點(diǎn)擊開(kāi)始游戲時(shí)使用這么一次。
這個(gè)游戲使用了一些函數(shù)來(lái)簡(jiǎn)化代碼和減少重復(fù),一些標(biāo)準(zhǔn)的數(shù)學(xué)函數(shù)用于 Clamp 和 Lerp 值。 ClampAngle 是有用的,因?yàn)樗?-PI 和 PI 之間 wrap angles,在許多游戲中已經(jīng)廣泛應(yīng)用。
R函數(shù)就像個(gè)魔術(shù)師,因?yàn)樗呻S機(jī)數(shù),通過(guò)取當(dāng)前隨機(jī)數(shù)種子的正弦,乘以一個(gè)大數(shù)字,然后看分?jǐn)?shù)部分來(lái)實(shí)現(xiàn)的。其實(shí)有很多方法可以做到,但這是最小的方法之一,而且對(duì)我們來(lái)說(shuō)也是足夠隨機(jī)。
我們將使用這個(gè)隨機(jī)生成器來(lái)創(chuàng)建各種程序,且不需要保存任何數(shù)據(jù)。例如,山脈、巖石和樹(shù)木的變化不用存到內(nèi)存。在這種情況下,目標(biāo)不是減少內(nèi)存,而是去除存儲(chǔ)和檢索數(shù)據(jù)所需的代碼。
因?yàn)檫@是一個(gè)“真正的3D”游戲,所以有一個(gè) 3D vector class 非常有用,它也能減少代碼量。這個(gè) class 只包含這個(gè)游戲必需的基本元素,一個(gè)帶有加法和乘法函數(shù)的 constructor 可以接受標(biāo)量或向量參數(shù)。為了確定標(biāo)量是否被傳入,我們只需檢查它是否小于一個(gè)大數(shù)。更正確的方法是使用 isNan 或者檢查它的類(lèi)型是否是 Vec3,但是這需要更多的存儲(chǔ)。
LSHA 通過(guò)模板字符串生成一組標(biāo)準(zhǔn)的 HSLA (色調(diào)、飽和度、亮度、alpha)顏色,并且剛剛被重新排序,所以更常用的 component 排在第一位。每過(guò)一關(guān)換一個(gè)整體色調(diào)也是通過(guò)這設(shè)置的。
DrawPoly 繪制一個(gè)梯形形狀,用于渲染場(chǎng)景中的一切。使用 |0 將 Ycomponent 轉(zhuǎn)換為整數(shù),以確保每段多邊形道路都能無(wú)縫連接,不然路段之間就會(huì)有一條細(xì)線。
DrawText 則用于顯示時(shí)間、距離和游戲標(biāo)題等文本渲染。
首先,我們必須生成完整的軌道,而且準(zhǔn)備做到每次游戲軌道都是不同的。如何做呢?我們建立了一個(gè)道路段列表,存儲(chǔ)道路在軌道上每一關(guān)卡的位置和寬度。軌道生成器是非?;A(chǔ)的操作,不同頻率、振幅和寬度的道路都會(huì)逐漸變窄,沿著跑道的距離決定這一段路有多難。
atan2 函數(shù)可以用來(lái)計(jì)算道路俯仰角,據(jù)此來(lái)設(shè)計(jì)物理運(yùn)動(dòng)和光線。
現(xiàn)在跑道就緒,我們只需要預(yù)置一些變量就可以開(kāi)始游戲了。
這是主要的更新功能,用來(lái)更新和渲染游戲中的一切!一般來(lái)說(shuō),如果你的代碼中有一個(gè)很大的函數(shù),這不是好事,為了更簡(jiǎn)潔易懂,我們會(huì)把它分幾個(gè)成子函數(shù)。
首先,我們需要得到一些玩家所在位置的道路信息。為了使物理和渲染感覺(jué)平滑,需要在當(dāng)前和下一個(gè)路段之間插入一些數(shù)值。
玩家的位置和速度是 3D 向量,并受重力、dampening 和其他因素等影響更新。如果玩家跑在地面上時(shí),會(huì)受到加速度影響;當(dāng)他離開(kāi)這段路時(shí),攝像機(jī)還會(huì)抖動(dòng)。另外,在對(duì)游戲測(cè)試后,我決定讓玩家在空中時(shí)仍然可以跑。
接下來(lái)要處理輸入指令,涉及加速、剎車(chē)、跳躍和轉(zhuǎn)彎等操作。雙擊通過(guò) mouseUpFrames 測(cè)試。還有一些代碼是來(lái)跟蹤玩家在空中停留了多少幀,如果時(shí)間很短,游戲允許玩家還可以跳躍。
當(dāng)玩家加速、剎車(chē)和跳躍時(shí),我通過(guò)spring system展示相機(jī)的俯仰角以給玩家動(dòng)態(tài)運(yùn)動(dòng)的感覺(jué)。此外,當(dāng)玩家駕車(chē)翻越山丘或跳躍時(shí),相機(jī)還會(huì)隨著道路傾斜而傾斜。
在渲染之前,canvas 每當(dāng)高度或?qū)挾缺恢卦O(shè)時(shí),畫(huà)布內(nèi)容就會(huì)被清空。這也適用于自適應(yīng)窗口的畫(huà)布。
我們還計(jì)算了將世界點(diǎn)轉(zhuǎn)換到畫(huà)布的投影比例。cameraDepth 值代表攝像機(jī)的視場(chǎng)(FOV)。這個(gè)游戲是90度。計(jì)算結(jié)果是 1/Math.tan(fovRadians/2) ,F(xiàn)OV 是90度的時(shí)候,計(jì)算結(jié)果正好是1。另外為了保持屏幕長(zhǎng)寬比,投影按 c.width 縮放。
空氣背景是用全屏的 linear gradient (徑向漸變)繪制的,它還會(huì)根據(jù)太陽(yáng)的位置改變顏色。
為了節(jié)省存儲(chǔ)空間,太陽(yáng)和月亮在同一個(gè)循環(huán)中,使用了一個(gè)帶有透明度的全屏 radial gradient(線性漸變)。
線性和徑向漸變相結(jié)合,形成一個(gè)完全包圍場(chǎng)景的天空背景。
山脈是通過(guò)在地平線上畫(huà)50個(gè)三角形,然后根據(jù)程序自己生成的。
因?yàn)橛昧斯饩€照明,山脈在面對(duì)太陽(yáng)時(shí)會(huì)更暗,因?yàn)樗鼈兲幱陉幱爸小4送?,越近的山脈顏色越暗,我想以此來(lái)模擬霧氣。這里我有個(gè)訣竅,就是微調(diào)大小和顏色的隨機(jī)值。
背景的最后一部分是繪制地平線,再用純綠填充畫(huà)布的底部。
在渲染道路之前,我們必須首先獲得投影的道路點(diǎn)。第一部分有點(diǎn)棘手,因?yàn)槲覀兊牡缆返?x 值需要轉(zhuǎn)換成世界空間位置。為了使道路看起來(lái)蜿蜒曲折,我們把x值作為二階導(dǎo)數(shù)。這就是為什么有奇怪的代碼“x+=w+=”出現(xiàn)的原因。由于這種工作方式,路段沒(méi)有固定的世界空間位置,每一幀都是根據(jù)玩家的位置重新計(jì)算。
一旦我們有了世界空間位置,我們就可以從道路位置中知道玩家的位置,從而得到本地?cái)z像機(jī)空間位置。代碼的其余部分,首先通過(guò)旋轉(zhuǎn)標(biāo)題、俯仰角來(lái)應(yīng)用變換,然后通過(guò)投影變換,做到近大遠(yuǎn)小的效果,最后將其移動(dòng)到畫(huà)布空間。
現(xiàn)在我們有了每個(gè)路段的畫(huà)布空間點(diǎn),渲染就相當(dāng)簡(jiǎn)單了。我們需要從后向前畫(huà)出每一個(gè)路段,或者更具體地說(shuō),連接上一路段的梯形多邊形。
為了創(chuàng)建道路,這里有4層渲染:地面,條紋路邊緣,道路本身和白色虛線。每一個(gè)都是基于路段的俯仰角和方向來(lái)加陰影,并且根據(jù)該層的表現(xiàn)還有一些額外的邏輯。
有必要檢查路段是在近還是遠(yuǎn)剪輯范圍,以防止渲染出現(xiàn) bug 。此外,還有一個(gè)很好的優(yōu)化方法是,當(dāng)?shù)缆纷兊煤苷瓡r(shí),可以通過(guò) distance 來(lái)減小道路的分辨率。如此,不僅減少了 draw count 一半以上,而且沒(méi)有明顯的質(zhì)量損失,這是一次性能勝利。
游戲有兩種不同類(lèi)型的物體:樹(shù)和石頭。首先,我們通過(guò)使用 R 函數(shù)來(lái)確定是否加一個(gè)對(duì)象。這是隨機(jī)數(shù)和隨機(jī)數(shù)種子特別有意思的地方。我們還將使用 R 為對(duì)象隨機(jī)添加不同的形狀和顏色。
最初我還想涉及其他車(chē)型,但為了達(dá)到 2KB 的要求,必須要進(jìn)行特別多的削減,因此我最后放棄了這個(gè)想法,用風(fēng)景作為障礙。這些位置是隨機(jī)的,也比較靠近道路,不然它們太稀疏,就很容易行駛。為了節(jié)省空間,對(duì)象高度還決定了對(duì)象的類(lèi)型。
這是通過(guò)比較玩家和物體在 3D 空間中的位置來(lái)檢查它們之間的碰撞位置。當(dāng)玩家撞到一個(gè)物體時(shí),玩家減速,該物體被標(biāo)記為“ hit ”,這樣它就可以安全通過(guò)。
為了防止對(duì)象突然出現(xiàn)在地平線上,透明度會(huì)隨著距離的接近而削弱。梯形繪圖函數(shù)定義物體的形狀和顏色,另外隨機(jī)函數(shù)會(huì)改變這兩個(gè)屬性。
游戲的標(biāo)題、時(shí)間和距離是用一個(gè)非?;A(chǔ)的字體渲染系統(tǒng)顯示出來(lái)的,就是之前設(shè)置的 DrawText 函數(shù)。在玩家點(diǎn)擊鼠標(biāo)之前,它會(huì)在屏幕中央顯示標(biāo)題。
按下鼠標(biāo)后,游戲開(kāi)始,然后 HUD 會(huì)顯示剩余時(shí)間和當(dāng)前距離。時(shí)間也在這塊更新,玩過(guò)此類(lèi)游戲的都知道,時(shí)間只在比賽開(kāi)始后減少。
在這個(gè) massive Update function 結(jié)束后,它調(diào)用 requestAnimationFrame (Update) 來(lái)觸發(fā)下一次更新。
HTML 需要一個(gè)結(jié)束腳本標(biāo)簽來(lái)讓所有的代碼能夠跑起來(lái)。
這就是整個(gè)游戲啦!下方的一小段代碼就是壓縮后的最終結(jié)果,我用不同的顏色標(biāo)注了不同的部分。完成所有這些工作后,你能感受到我在2KB內(nèi)就做完了整個(gè)游戲是多么讓我滿意了嗎?而這還是在zip之前的工作,zip還可以進(jìn)一步壓縮大小。
當(dāng)然,還有很多其他 3D 渲染方法可以同時(shí)保證性能和視覺(jué)效果。如果我有更多的可用空間,我會(huì)更傾向于使用一個(gè) WebGL API 比如 three.js ,我在去年制作的一個(gè)類(lèi)似游戲“Bogus Roads”中用過(guò)這個(gè)框架。此外,因?yàn)樗褂玫氖?requestAnimationFrame ,所以需要一些額外的代碼來(lái)確保幀速率不超過(guò)60 fps,增強(qiáng)版本中我會(huì)這么用,盡管我更喜歡使用 requestAnimationFrame 而不是 setInterval ,因?yàn)樗谴怪蓖诘模╒Syn,VerticalSynchronization),所以渲染更絲滑。這種代碼的一個(gè)主要好處是它非常兼容,可以在任何設(shè)備上運(yùn)行,盡管在我舊 iPhone 上運(yùn)行有點(diǎn)慢。
游戲代碼被我放到了 GitHub 上的 GPL-3.0 下(
https://github.com/KilledByAPixel/HueJumper2k),所以你可以在自己的項(xiàng)目中自由使用它。該庫(kù)中還包含 2KB 版本的游戲,準(zhǔn)確說(shuō)是2031字節(jié)!歡迎你添加一些其他的功能,比如音樂(lè)和音效到“增強(qiáng)”版本中。
雷鋒網(wǎng)注意到,F(xiàn)rank Force 在個(gè)人博客發(fā)了這篇文章后,在內(nèi)容、標(biāo)題的加持下,這篇文章后來(lái)被不少?lài)?guó)外媒體轉(zhuǎn)載。在盛贊之余,也有質(zhì)疑的聲音。網(wǎng)友“Anon”在原文下評(píng)論:你是如何在 2KB 安裝一個(gè)完整的 javascript 的,除非你可以隨意忽略 dependencies 插件庫(kù)的大小,或者你將整個(gè)游戲作為 dependency,大小才有可能控制到 2KB,否則就是欺騙。
Frank 回復(fù)表示,大多數(shù) small demos 都需要某種運(yùn)行環(huán)境,即使它是可執(zhí)行的。在這種情況下,就是 javascript 運(yùn)行時(shí)環(huán)境,沒(méi)有其他 dependencies.。因?yàn)?javascript 是解釋的,所以也可以說(shuō)壓縮后的代碼是在2KB以?xún)?nèi)的。
雷鋒網(wǎng)發(fā)現(xiàn),有其他網(wǎng)友表示認(rèn)可 Frank 的說(shuō)法,他們認(rèn)為 JS 是一種解釋語(yǔ)言,不能將其與其他編譯語(yǔ)言相比較。
雷鋒網(wǎng)
熱門(mén)資訊
探討游戲引擎的文章,介紹了10款游戲引擎及其代表作品,涵蓋了RAGE Engine、Naughty Dog Game Engine、The Dead Engine、Cry Engine、Avalanche Engine、Anvil Engine、IW Engine、Frostbite Engine、Creation引擎、Unreal Engine等引擎。借此分析引出了游戲設(shè)計(jì)領(lǐng)域和數(shù)字藝術(shù)教育的重要性,歡迎點(diǎn)擊咨詢(xún)報(bào)名。
2. 手機(jī)游戲如何開(kāi)發(fā)(如何制作傳奇手游,都需要準(zhǔn)備些什么?)
?如何制作傳奇手游,都需要準(zhǔn)備些什么?提到傳奇手游相信大家都不陌生,他是許多80、90后的回憶;從起初的端游到現(xiàn)在的手游,說(shuō)明時(shí)代在進(jìn)步游戲在更新,更趨于方便化移動(dòng)化。而如果我們想要制作一款傳奇手游的
3. B站視頻剪輯軟件「必剪」:免費(fèi)、炫酷特效,小白必備工具
B站視頻剪輯軟件「必剪」,完全免費(fèi)、一鍵制作炫酷特效,適合新手小白。快來(lái)試試!
4. Steam值得入手的武俠游戲盤(pán)點(diǎn),各具特色的快意江湖
游戲中玩家將面臨武俠人生的掙扎抉擇,戰(zhàn)或降?殺或放?每個(gè)抉定都將觸發(fā)更多愛(ài)恨糾葛的精彩奇遇?!短烀嬗肪哂卸嗑€劇情多結(jié)局,不限主線發(fā)展,高自由...
5. Bigtime加密游戲經(jīng)濟(jì)體系揭秘,不同玩家角色的經(jīng)濟(jì)活動(dòng)
Bigtime加密游戲經(jīng)濟(jì)模型分析,探討游戲經(jīng)濟(jì)特點(diǎn),幫助玩家更全面了解這款GameFi產(chǎn)品。
6. 3D動(dòng)漫建模全過(guò)程,不是一般人能學(xué)的會(huì)的,會(huì)的多不是人?
步驟01:面部,頸部,身體在一起這次我不準(zhǔn)備設(shè)計(jì)圖片,我從雕刻進(jìn)入。這一次,它將是一種純粹關(guān)注建模而非整體繪畫(huà)的形式。像往常一樣,我從Sphere創(chuàng)建它...
7. 3D動(dòng)畫(huà)軟件你知道幾個(gè)?3ds Max、Blender、Maya、Houdini大比拼
當(dāng)提到3D動(dòng)畫(huà)軟件或動(dòng)畫(huà)工具時(shí),指的是數(shù)字內(nèi)容創(chuàng)建工具。它是用于造型、建模以及繪制3D美術(shù)動(dòng)畫(huà)的軟件程序。但是,在3D動(dòng)畫(huà)軟件中還包含了其他類(lèi)型的...
8. 3D打印技巧揭秘!Cura設(shè)置讓你的模型更堅(jiān)固
想讓你的3D打印模型更堅(jiān)固?不妨嘗試一下Cura參數(shù)設(shè)置和設(shè)計(jì)技巧,讓你輕松掌握!
9. 開(kāi)發(fā)三昧游戲叫什么(三昧動(dòng)漫)
?三昧動(dòng)漫對(duì)于著名ARPG游戲《巫師》系列,最近CD Projekt 的高層回應(yīng)并不會(huì)推出《巫師4》。因?yàn)椤段讕煛废盗性诓邉澋臅r(shí)候一直定位在“三部曲”的故事框架,所以在游戲的出品上不可能出現(xiàn)《巫師4》
10. 虛幻引擎5節(jié)省存儲(chǔ)空間用這招!緩存的清理與設(shè)置
眾所周知,虛幻引擎5(下面簡(jiǎn)稱(chēng)UE5)特別占用存儲(chǔ)空間,僅一個(gè)版本安裝好的文件就有60G,這還不包括我們?cè)谑褂脮r(shí)保存的工程文件和隨之產(chǎn)生的緩存文件。而...
最新文章
同學(xué)您好!