Unity老游戲復(fù)刻計(jì)劃之《超級(jí)馬里奧兄弟》(1)
(本文作者Khalil)
大家好我又來(lái)了。
超級(jí)瑪麗是大多數(shù)8090、還有少數(shù)00年的朋友都耳熟能詳?shù)挠螒?。?dāng)然這個(gè)翻譯來(lái)自于早年的港臺(tái)盜版商,人家原名叫做《超級(jí)馬里奧兄弟(Super Mario Bros)》,1985年由任天堂出品。

等等。
1985年什么概念?
我是肯定還沒出生的了。
我的家鄉(xiāng)居然都還沒建?。。o(wú)獎(jiǎng)競(jìng)猜:能猜到是哪個(gè)省嗎?)
雖然嚴(yán)格說(shuō),本作并非馬里奧的首次登場(chǎng)(在這之前還有不帶“超級(jí)”倆字兒的《馬里奧兄弟》,更之前還有雖然沒上標(biāo)題但擔(dān)當(dāng)實(shí)質(zhì)主角的《大金剛》),但要論在游戲史上的貢獻(xiàn),本作的意義絕對(duì)非同尋常。

隨便一列就能列出一大堆標(biāo)志性特征:
劃時(shí)代的橫向卷軸推進(jìn)方式;
極其良好的操作反饋和手感(物理引擎?なんだそりゃ);
前平后陡的難度曲線;
麻雀雖小五臟俱全的音樂(lè)音效設(shè)計(jì);
異常豐富的隱藏要素;

如此等等。另外,和當(dāng)年的大多數(shù)游戲一樣,它是個(gè)輪換制的雙人游戲——不能倆人同時(shí)玩、只能一個(gè)玩家“死”了之后換成另一個(gè)玩家。所以我小時(shí)候不喜歡這個(gè)游戲,因?yàn)槲夷菚?huì)兒太菜了,第一關(guān)都過(guò)不去,只能看著我的小伙伴玩到通關(guān),體驗(yàn)極差(喂

似乎鋪墊多了點(diǎn)?
入正題入正題,講講我是怎么來(lái)嘗試復(fù)刻的吧。
首先還是準(zhǔn)備好素材,我的常規(guī)流程:
去素材網(wǎng)站上找到相應(yīng)的素材,然后通過(guò)P圖網(wǎng)站把需要的精靈圖摳出來(lái),在Unity中把需要的動(dòng)畫制作好。
素材準(zhǔn)備好后,先鋪設(shè)一部分地面讓角色得以站立,然后把角色的腳本、碰撞體、剛體掛上。
接著——去玩原版游戲。反復(fù)玩。

當(dāng)然可不是亂玩,這是在挖細(xì)節(jié)。復(fù)刻游戲,最重要的是當(dāng)玩家一上手就會(huì)脫口而出:“有內(nèi)味兒了”。所以為了盡量和原版游戲貼近,我要像QA一樣去逐個(gè)測(cè)試游戲的功能,玩家做了什么之后游戲內(nèi)會(huì)呈現(xiàn)出什么效果,然后通過(guò)文字、圖片和錄屏記錄下來(lái),反復(fù)琢磨。琢磨清楚了再上手實(shí)現(xiàn)。
角色左右移動(dòng)和動(dòng)畫,通過(guò)改變localScale來(lái)實(shí)現(xiàn)人物的轉(zhuǎn)向效果,這兩點(diǎn)實(shí)在是簡(jiǎn)單得一匹就不多說(shuō)了。
接下來(lái)是跳躍。原版馬里奧得跳躍機(jī)制,是按的時(shí)間越長(zhǎng)跳的越高,小小的跳躍有大大的區(qū)別。這也是裝飾手感的重要環(huán)節(jié)。這個(gè)效果的實(shí)現(xiàn)不難,下面鏈接介紹了一個(gè)好辦法:
blog.csdn.net/qq_62440805/article/details/124799615
文中寫到,馬里奧起跳和下落的速度不同,并且長(zhǎng)按跳躍鍵能跳的更高,這個(gè)效果通過(guò)改變剛體的重力加速度即可實(shí)現(xiàn)。起初我合計(jì):這代碼剛好契合需求嘛!簡(jiǎn)直是打瞌睡送來(lái)了枕頭,我就直接照抄了下來(lái),調(diào)整了一下數(shù)值,跳躍的效果就和原版游戲很接近了。
但這時(shí)候我尚未理解其中含義,只是單純的面向CV編程。馬老師review了一下后,讓我把游戲中的重力關(guān)了,免得影響代碼的效果。由于我沒完全理解代碼具體的意思所以沒get到馬老師的意思,關(guān)掉重力人咋動(dòng)???
后來(lái)還是在馬老師的指導(dǎo)下優(yōu)化了代碼且理清了思路。上面文章中寫的代碼本質(zhì)是在三種不同情況下改變物體所受的重力大小,從而形成不同的效果。在老師改進(jìn)的代碼中,在FixedUpdate中寫到:下落時(shí)要加大所受重力(設(shè)重力gravityScale為3),使角色更快掉落,在按住跳躍鍵時(shí),所受重力為1,松開時(shí)所受重力為2(也可能更追求細(xì)節(jié)的朋友覺得還不夠貼合原版游戲,可以慢慢多調(diào)試):

跳躍的速度是不變的,則跳躍后所受重力越大,跳的就會(huì)越矮,下落得就會(huì)越快。在這里告誡自己也提醒大家,寫代碼的時(shí)候思路一定要靈動(dòng)、清晰,不要死板,不要照抄沒理解透的代碼。
跳躍的動(dòng)畫比起其他游戲就簡(jiǎn)單了,由于下落滯空跳躍全是一個(gè)動(dòng)作,所以只要角色不在地上,它就保持跳躍動(dòng)作就ok了。

人物可以正常移動(dòng)后就開始關(guān)卡搭建。
進(jìn)入游戲,往前走幾步會(huì)遇到”?”磚塊,跳躍從下面將它頂起,它會(huì)彈出金幣并且給我們加上200分,然后變?yōu)椴粫?huì)被頂起的空磚塊。這個(gè)效果起初我是這樣實(shí)現(xiàn)的:

這樣做,效果雖然是有了,不過(guò)步驟太亂,十分麻煩,且代碼死板笨拙。如果繼續(xù)再往下做,要是之后這一塊再出現(xiàn)問(wèn)題,別說(shuō)解決了,可能連問(wèn)題從哪出來(lái)的都不知道。
馬老師看了我的做法后提醒我,游戲就是造假的藝術(shù)。
確實(shí),如果游戲全部做成”真的”,可能反而在游戲世界里展現(xiàn)出來(lái)的效果對(duì)于玩家來(lái)說(shuō)卻是很假、很怪的,所以為了游戲效果更“真實(shí)”,我們游戲制作者需要的不是去完全模仿真實(shí)的情況去實(shí)現(xiàn)功能,因?yàn)橛螒虮緛?lái)就不是真實(shí)的,也永遠(yuǎn)做不到和現(xiàn)實(shí)完全一樣,按現(xiàn)實(shí)中的思路做反而會(huì)失真,特別是我這樣的做法復(fù)用性太差太麻煩、后續(xù)產(chǎn)生問(wèn)題的話難以解決。我們需要的是開大腦洞,用“造假”來(lái)模擬出效果。
我之前的想法太死板,我想著既然這磚塊要跳,那我就給它塊地板讓它跳就好了,但是我沒考慮到原版游戲中,磚塊仿佛就是世界中的固定存在,玩家做什么都無(wú)法影響它的存在,他就在那個(gè)固定的位置,無(wú)論你頂它多少次,它還是在那個(gè)位置——除了變大后的“超級(jí)馬里奧”可以頂壞某些磚塊。

仔細(xì)想想,如果它真的會(huì)被玩家頂起來(lái),玩家還會(huì)被他擋住嗎?玩家應(yīng)該可以繼續(xù)向上跳躍吧,但是在原版游戲中,就算磚塊被頂起來(lái)了,玩家還是會(huì)被磚塊擋住、不能繼續(xù)向上跳了。如果按我的做法,玩家能給磚塊施加力讓它彈起,那么磚塊也應(yīng)該給玩家施加一個(gè)力讓玩家馬上下落,才能達(dá)到讓玩家被磚塊“擋住”的效果。
馬老師提供了一種思路:磚塊只需要有碰撞體,不需要?jiǎng)傮w。沒有剛體它就會(huì)懸在固定位置不會(huì)動(dòng),也就不需要考慮玩家受到磚塊的力,突變?yōu)閡磚塊的碰撞體它就在那不動(dòng),玩家只能被擋住。
那么磚塊被頂起來(lái)的效果怎么實(shí)現(xiàn)呢?
一個(gè)方法是只要移動(dòng)貼圖即可,通過(guò)協(xié)程實(shí)現(xiàn)。問(wèn)號(hào)磚塊被撞到變?yōu)榭沾u塊后,貼圖每幀移動(dòng)一小段距離,一共四幀,這樣就可以呈現(xiàn)出磚塊被頂起然后下落的簡(jiǎn)單視覺效果了。
聽起來(lái)很簡(jiǎn)單,我現(xiàn)在來(lái)看馬老師幫忙寫的示例代碼也覺得很簡(jiǎn)單,不過(guò)對(duì)新手來(lái)說(shuō),做的時(shí)候極有可能想不到。就和看推理小說(shuō)里的偵探破案一樣,偵探撥開重重迷霧,向大家展示出了案件的原貌,但是最終案件結(jié)束,大家都置于案件外了,這時(shí)候明白了偵探的推理方法后,都會(huì)覺得“好簡(jiǎn)單,我好像也能做到”。其實(shí)難的不是拿到一個(gè)個(gè)線索后推理出案件的真相,而是找到線索的方法和正確的推理方式。通過(guò)老師提供的優(yōu)秀思路來(lái)寫出好的代碼固然簡(jiǎn)單,難的是在沒有老師幫助的情況下該如何寫出好代碼。
等等等等。為什么是“馬老師幫忙寫的代碼”?

說(shuō)來(lái)慚愧,因?yàn)槲覍懙拇a過(guò)于繁雜多余,馬老師用他的方式來(lái)對(duì)我的代碼進(jìn)行了優(yōu)化。

因?yàn)槲业姆椒ū緛?lái)就是“急中生智”,思路沒完全捋清,所以我對(duì)它的記憶現(xiàn)在幾乎完全消散了,畢竟程序員昨天寫好的代碼今天都不一定看得懂(狗頭)。
我就講講在以我的Bad方法為基底的情況下老師改良后的做法:
在這里需要注意的是,協(xié)程是由Bounce方法開啟的,Bounce方法又是由磚塊調(diào)用的,而磚塊的BeHit方法又是由角色腳本通過(guò)射線檢測(cè)調(diào)用的,層層嵌套。而射線檢測(cè)是持續(xù)的,如果玩家跳起來(lái)之后射線持續(xù)接觸到磚塊,可能會(huì)開啟多次協(xié)程,互相沖突后出現(xiàn)問(wèn)題。這里我們可以給定磚塊一個(gè)名為isUp的bool變量,初始值為false,在Bounce方法中:如果isUp為true,則return,若是磚塊已經(jīng)被頂起,則isUp為true,在JackUp中,貼圖位置移動(dòng)的兩個(gè)循環(huán)都退出后,再將isUp恢復(fù)為false,這樣Bounce和JackUp方法就不會(huì)在一次彈跳完成之前被重復(fù)調(diào)用了。


對(duì)比一下咱們能感覺出區(qū)別不算太大,有點(diǎn)“內(nèi)味兒了”的感覺了。
由于進(jìn)度和篇幅問(wèn)題,本篇就講到這(本篇沒有工程)
下篇會(huì)講到玩家吃蘑菇變形態(tài)、怪物的制作和生成的實(shí)現(xiàn),大家敬請(qǐng)期待。
歡迎加入游戲開發(fā)群歡樂(lè)攪基:1082025059
對(duì)學(xué)習(xí)游戲開發(fā)、游戲制作感興趣的童鞋,歡迎訪問(wèn)咱們的主頁(yè):http://levelpp.com/