這篇文章默認大家已經(jīng)對Unity3D和PlayMaker有一定的了解,所以沒有把所有操作步驟都寫的非常詳細,不太看得明白的同學可以先行閱讀:
- PlayMaker入門介紹
- Unity3D的數(shù)據(jù)類型以及PlayMaker的變量
- PlayMaker:觸發(fā)事件
- PlayMaker簡單實例(1):PM_Cube
- PlayMaker簡單實例(2):角色與攝影機的運動控制
視頻教程已經(jīng)上傳YouTube:[教程]使用PlayMaker重制Unity官方“滾動小球”教程
視頻文件及素材資源下載地址在:百度網(wǎng)盤
Unity3D官方教程是非常非常優(yōu)秀的新手入門學習資源,將其PlayMaker化是一個挺有趣的體驗。
本教程是Unity官方教程第一篇,原教程地址:Roll-a-ball tutorial
準備場景
Roll-a-ball的場景很簡單,一塊2×1×2的藍色Plane做地面(命名為Ground
),四個0.5×1×20.5的Cube做墻壁(放在Walls
空物體之中),墻壁物體距離坐標原點10單位長度,一個1×1×1的Sphere作為玩家操控的小球(命名為Player
)。
設(shè)置Main Camera的位置和旋轉(zhuǎn),修改Directional Light的位置與旋轉(zhuǎn),得到合適的攝影機位置以及光照效果。
添加小球運動
給Player添加一個Rigidbody組件。
Rigidbody將游戲物體變成一個“主動剛體”,使其可以受到力的作用而改變其位置和旋轉(zhuǎn),并且會根據(jù)其碰撞體(Collider)組件的設(shè)置而與其他物體相互作用。
Unity中的剛體動力學是這樣劃分的:
- 沒有添加Collider組件的游戲物體不參與動力學計算;
- 只有Collider組件且沒有勾選Is Trigger選項的游戲物體被認為是“被動剛體”,不會被動力學改變其運動狀態(tài),也就是說不會被撞開;
- 勾選了Is Trigger選項的Collider不會起到阻擋其他Collider的作用,也就是不會發(fā)生碰撞現(xiàn)象,但依然能夠感知到其與其他Collider之間發(fā)生的交互作用,比如有其他Collider進入、停留或離開該Collider范圍時,該Collider是能夠感知到的;
- 有Collider組件也有Rigidbody組件的游戲物體被認為是“主動剛體”,主動剛體會受到動力學作用而改變運動狀態(tài),我們可以給主動剛體施加作用力或設(shè)置運動速度,也可以用其他剛體去撞擊主動剛體;
- 勾選了Use Gravity(使用重力)選項的Rigidbody會持續(xù)受到重力作用而向-Y方向運動,除非被其他剛體阻擋;
- 勾選了Is Kinematic(運動學)選項的Rigidbody將忽略外力作用,也就是說重力和剛體撞擊都不會使其產(chǎn)生運動,但我們直接設(shè)置其速度(velocity)來讓其運動起來,算是一種特殊的“主動剛體”吧。
為Player添加Fsm,在State 1中添加Get Axis Vector以獲得輸入向量,添加Add Force為Player施加作用力。
與控制角色運動不同,控制小球運動我們不僅希望改變其位置,還希望小球能夠“滾動”起來,這時候使用“施加作用力”的方式就比“設(shè)定運動速度”的方式要合適一些。并且,通過“施加作用力”來控制物體(尤其是球形物體)并不能非常精確的控制對象的運動(因為有慣性作用),所以用在這里反而能給游戲添加一些難度和樂趣。
這里我指定了兩個變量,一個speed變量用來設(shè)置小球的運動速度,一個input axis變量用來儲存輸入向量值。speed變量勾選了Inspector選項,使其可以在Inspector中可見。
添加可拾取的小方塊(Pickup)
在場景中新建一個Cube(改名為Pickup),修改其位移屬性為<<0, 1, 0>>,修改其旋轉(zhuǎn)屬性為<<45, 45, 45>>,修改其縮放屬性為<<0.5, 0.5, 0.5>>,添加一個黃色材質(zhì)給Pickup。
為Pickup添加一個Fsm,在State 1添加一個Rotate:
勾選了Every Frame選項之后,Rotate行為可以按照Vector參數(shù)中所設(shè)定的旋轉(zhuǎn)速度,持續(xù)旋轉(zhuǎn)指定游戲物體。為了獲得平滑的旋轉(zhuǎn)效果,可以勾選Per Second選項和Fixed Update選項,讓其按照“每秒多少度”的方式來進行旋轉(zhuǎn)。
這里我沒有直接輸入旋轉(zhuǎn)速度,而是通過新建一個名稱為rotate speed的變量來控制,rotate speed變量的初始值設(shè)置為<<15, 30, 45>>,并在Inspector中可見。
將設(shè)置好的Pickup物體轉(zhuǎn)換成prefab,然后在場景中復制擺放。新建一個名為Pickups的空物體來放置所有的Pickup物體,以保持場景整潔。
小球“拾取”黃色方塊
Roll A Ball小游戲的基本交互邏輯是:玩家控制小球在場景中“拾取”所有的黃色小方塊。為了達成這個目的,我們需要進行“碰撞”判定:當小球的碰撞體與黃色方塊的碰撞體相交時,黃色方塊消失。
由于小球和黃色方塊上都具有Collider組件,所以這個判定工作可以被放在小球上,也可以被放在黃色小方塊上。在這個范例中我們選擇將判定工作放在小球上,是因為這個判定是每幀執(zhí)行的,如果放在小方塊上,則所有的小方塊都會每幀執(zhí)行判定操作,顯然執(zhí)行效率不如將判定放在小球這一個物體上。
為Player添加一個Trigger Event,設(shè)定其當有其他Trigger進入小球的碰撞體范圍時,將這個Trigger物體儲存在一個叫做pickup object的變量中,并觸發(fā)hit a pickup事件。
為了避免別的trigger物體的干擾,我們設(shè)定這個Trigger Event行為僅針對那些被Tag為Pickup的Trigger物體起效果。因此,我們需要添加一個名為Pickup的Tag,并將所有的Trigger物體標記上這個Tag。
當hit a pickup事件被觸發(fā)時,Player的Fsm狀態(tài)從State 1跳轉(zhuǎn)為State 2。
在State 2中,我們添加一個Activate Game Object,設(shè)置pickup object變量中所儲存的游戲物體(也就是進入了Player碰撞體的Trigger物體)的Activate屬性進行取消操作。
Activate實際上就是游戲物體Inspector面板中名稱前面的小勾選框,如果取消勾選,這個游戲物體就“隱藏”了,或者說“失效”了,雖然依然存在于游戲場景中,卻不會起到任何作用。
同時,我們還需要將Pickup物體(黃色小方塊)的碰撞體設(shè)置為Trigger類型(勾選Box Collider組件中的Is Trigger選項),并設(shè)置其為Kinematic(勾選Rigidbody組件中的Is Kinematic選項)。
將小方塊設(shè)置為Trigger類型是因為我們不希望小球和方塊發(fā)生實際碰撞,只希望檢測到雙方相互觸碰而已;將小方塊設(shè)置為Kinematic是因為我們不希望小球在添加了Rigidbody之后受到重力作用而改變其“浮空自動旋轉(zhuǎn)”的運動狀態(tài)設(shè)置。
注意,如果是在場景中對Pickup物體進行的修改,需要點擊Apply按鈕將其修改應(yīng)用到其他所有Pickup物體上,或者直接對prefab物體進行修改。
運行測試,我們現(xiàn)在可以用小球“吃掉”所有的黃色小方塊了。
添加UI
我們希望每“吃掉”一個小方塊,就能夠得到1分,“吃完”所有的小方塊,就可以贏得游戲!
在場景中添加兩個UI Text物體:Score Text和Win Text:
可以看到,系統(tǒng)自動創(chuàng)建了一個Canvas,并將UI Text物體放置在Canvas中。同時,系統(tǒng)還創(chuàng)建了一個EventSystem,這兩個自動創(chuàng)建的節(jié)點都不要隨便刪掉。
修改兩個UI Text物體的參數(shù),獲得如下UI顯示:
注意,這時候我們在Inspector中輸入的UI文字內(nèi)容只是一個預設(shè)值,我們后面會用PlayMaker控制其中文字內(nèi)容的。
在Fsm中控制UI顯示內(nèi)容
這時候需要對Player的Fsm進行一些較大的修改了。
首先,State 2中需要加入“計分”的功能,并判斷是否所有的Pickup物體都被“吃掉”了。如果是,則跳轉(zhuǎn)到State 4(游戲獲勝狀態(tài)),如果否,則跳轉(zhuǎn)回State 1繼續(xù)游戲。同時,State 2中還需要進行UI文字內(nèi)容更新操作,使其顯示最新的分數(shù)。
其次,需要制作游戲獲勝狀態(tài)State 4,這里需要顯示W(wǎng)in Text。
最后,需要在State 1之前添加一個State 3,在State 3中做一些必要的預設(shè)工作。
在State 3中:
- 用Set Int Value設(shè)置一個score變量,并將其數(shù)值初始化為0;
- 用Set Game Object設(shè)置一個score text變量,并指定Score Text物體給score text變量;
- 用Set Game Object設(shè)置一個win text變量,并指定Win Text物體給win text變量;
- 用Convert Int To String將score變量值轉(zhuǎn)換成一個字符串變量score string,以便指定為UI文字的內(nèi)容;
- 用U Gui Text Set Text將score text物體的Text參數(shù)值設(shè)置為“ ”(也就是無內(nèi)容,如果什么字都不打PlayMaker會報錯,所以打個空格就好了);
- 用U Gui Text Set Text將win text物體的Text參數(shù)值也設(shè)置為“ ”。
注意:U Gui Text Set Text行為需要自行安裝uGui行為包。
在State 2中:
- 添加Int Add讓score數(shù)值加1,代表得到1分;
- 添加Int Compare比較新的score數(shù)值和一個total pickups變量(手動設(shè)置為12)中的數(shù)值誰大誰小,如果score >= total pickups,則觸發(fā)win事件(跳轉(zhuǎn)到State 4);
- 添加Convert Int To String將score變量值轉(zhuǎn)換成一個字符串變量score string,設(shè)置Format參數(shù)使其顯示為“Score: 1”這樣的格式;
- 添加U Gui Text Set Text將score string指定給score text物體的Text參數(shù)值,更新score text的顯示內(nèi)容。
注意,Int Compare放在Convert Int To String前面,當“吃到”最后一個小方塊的時候,score text不會更新為Score: 12,因為已經(jīng)直接跳轉(zhuǎn)到State 4了。如果希望在State 4的時候能看到Score: 12這樣的分數(shù)顯示,則需要將Int Compare放在U Gui Text Set Text的后面。
在State 4中:
- 添加U Gui Text Set Text將win text的文字直接設(shè)置成“You Win!”。
這里其實偷了個巧,懶得去做顯示隱藏Win Text物體的操作,就直接用“ ”來假裝Win Text未出現(xiàn)。
最終的UI顯示效果如下:
到此為止,就完成了Unity官方Roll A Ball教程的PlayMaker化!
一些小改進
發(fā)布游戲之前,添加了一些小改進:
- 給小球添加了貼圖,可以清晰看到小球的旋轉(zhuǎn);
- 添加了一個RESTART按鈕讓玩家可以重新開始游戲;
- 烘焙了光照貼圖;
- 添加了一點點后期效果:抗鋸齒、邊緣暗化;
- 添加了背景音樂和“吃掉”小方塊時的音效。
WebGL版游戲演示:Roll A Ball
其他的設(shè)計思路
Roll A Ball小游戲非常簡單,但我們可以在這個基礎(chǔ)上開開腦洞,設(shè)計一些其他玩法,比如:
- 添加復雜的墻壁,修改小方塊布局,以得到不同的關(guān)卡設(shè)計;
- 設(shè)置墻壁不可觸碰,碰到墻壁則重置游戲;
- 規(guī)定時限,時限內(nèi)沒有吃到所有的小方塊則任務(wù)失敗;
- 添加陷阱,比如地面有個洞;
- 添加機關(guān)、道具,比如傳送門;
- 為小球添加跳躍行為;
- 添加坡度,將目前的平面關(guān)卡設(shè)計轉(zhuǎn)換為立體關(guān)卡設(shè)計;
- ……