【菜菜丸的菜鳥教程】“擲”出個未來——擲骰子教程
擲骰子是很常見的游戲基礎(chǔ)功能,尤其是在棋牌游戲中有廣泛應(yīng)用。本教程將從原理開始,教大家制作簡單的擲骰子功能,并幫助新手避開寫腳本時容易掉的坑。本教程使用的引擎為2021.3,但原理也適用于之前的Unity版本。
?
一、場景和UI搭建
擲骰子對場景的要求不是很高,想簡化這一步的小伙伴只需在場景中添加一個平面(Plane)?;蛘咦鲆粋€類似于下圖的“模擬桌面”,即一個Plane加上四邊的圍擋,用于防止擲骰子時骰子跑到桌面之外。大小根據(jù)你的需求來設(shè)置即可。注意,桌面和圍擋務(wù)必要帶適合其形狀的碰撞體(Box Collider)。

UI制作也很簡單。首先在Hierarchy窗口單擊右鍵>UI>Canvas,創(chuàng)建畫布。在Canvas下單擊右鍵>UI>Text-TextMeshPro,新建一個TextMeshPro文本對象。在這個文本對象Inspector窗口的Text Input欄中,輸入文字“您擲出的點數(shù)是:”,并按需求調(diào)整文字的大小、顏色、位置。
然后,復(fù)制該文本對象,重命名為“Number Text”,拖至剛才那段文本右側(cè),在Text Input欄中輸入測試文字“0”,稍后我們將用它來顯示擲出的骰子點數(shù)。為了讓結(jié)果更顯眼,可以把它的字體調(diào)得大一些,并勾選Main Settings中的Color Gradient,給它加上漸變色。

最后是導(dǎo)入骰子素材,教程中使用的骰子模型是從Unity資源商店下載的免費素材,地址為:https://assetstore.unity.com/packages/3d/props/tools/dice-d6-game-ready-pbr-200151將骰子模型導(dǎo)入場景后,記得給它添加RigidBody組件和Box Collider組件。
完成的場景和UI如下圖所示:

二、思路分析
(一)實現(xiàn)擲骰子的思路
提到擲骰子,很多小伙伴會想到,應(yīng)該使用Rigidbody.AddForce方法,給骰子剛體施加一個向上的Impulse類型的力。這當然沒錯,但是單單使用這個方法,骰子只會直上直下,點數(shù)不會發(fā)生變化。即使給施力方向添加一些隨機性,效果也很不真實。這時我們需要用到另一個方法,即Rigidbody.AddTorque,它可以給骰子剛體在不同軸向上施加扭矩,使骰子在拋起的同時進行旋轉(zhuǎn),這樣我們就可以更真實地模擬擲骰子的效果了。
(二)獲取骰子點數(shù)的思路
在講這部分思路前,請大家打開自己的工程,對照著你的骰子來看,會更容易理解。
在擲骰子這個過程中,會經(jīng)歷骰子從靜到動,再停下來的過程。所以,骰子的位置會有變化,我們要獲得骰子停下來后朝上那一面所顯示的點數(shù)。那么,如何才能獲得這個點數(shù)呢?我們可以用骰子的頂點來獲取它。骰子的每個面均有四個頂點,只要確定每個面對應(yīng)的這些頂點,擲完骰子后找出位于骰子頂面(y坐標較大)的四個頂點,它們所圍成的面上的點數(shù)就是我們要獲取的點數(shù)。
以下圖這種放置方式的骰子為例。假設(shè)它是骰子的初始狀態(tài),我們分別用0、1、2、3標出底面的頂點,用3、4、5、6標出頂面的頂點。觀察之后,將能得到以下對應(yīng)關(guān)系:
1點所在的面對應(yīng)的四個頂點是:0、1、4、5;
2點所在的面對應(yīng)的四個頂點是:4、5、6、7;
3點所在的面對應(yīng)的四個頂點是:1、2、5、6;
4點所在的面對應(yīng)的四個頂點是:0、3、4、7;
5點所在的面對應(yīng)的四個頂點是:0、1、2、3;
6點所在的面對應(yīng)的四個頂點是:2、3、6、7。

請大家記錄下這個對應(yīng)關(guān)系和空間位置圖形,稍后寫腳本時會用到。
三、編寫腳本
創(chuàng)建一個名為Dice的腳本,掛載到游戲?qū)ο驞ice上。
(一)拋起骰子
前面已經(jīng)分析了拋骰子需要使用的函數(shù)方法,實現(xiàn)起來就很簡單了。腳本如下:
現(xiàn)在回到游戲中,按空格鍵就可以拋起骰子了。請注意,這里對力和扭矩都賦予了一定隨機性,大家可以根據(jù)自己的需求調(diào)整具體數(shù)值。
(二)獲得初始時骰子的局部坐標
Unity提供了獲取碰撞體大小以及中點局部坐標的方法,即Bound.size和Bounds.center,結(jié)合之前畫的示意圖,我們可以輕松得到初始狀態(tài)下骰子八個頂點的局部坐標。
(三)獲得骰子落下后的頂點坐標
這一步稍微麻煩一些,因為骰子落回桌面后,不會馬上停下來,有時還會在桌面上翻滾。因此,不能用碰撞檢測作為取得骰子坐標的判斷條件。當骰子徹底停下來時,它的速度和角速度都會變?yōu)榱悖覀兙陀盟鼇磉M行判斷。此外,當游戲剛開始時,骰子的速度和角速度也為零,所以我們要通過一個布爾值來排除該情況。
確定骰子停止后,我們要將骰子的所有頂點轉(zhuǎn)換成世界坐標,用到的方法是Transform.TransformPoint(詳見https://docs.unity3d.com/cn/2021.1/ScriptReference/Transform.TransformPoint.html)
這一步的腳本如下:
(四)獲得骰子點數(shù)
現(xiàn)在,我們已經(jīng)知道骰子每個面對應(yīng)的頂點以及結(jié)束時的骰子頂點世界坐標。那么,只要找出此時骰子頂面的頂點有哪些,就可以獲得骰子點數(shù)了。第二步介紹了一種判斷頂面頂點的方法,這里還可以使用另一種方法:只要某個頂點的y值大于它對面頂點的y值,那么就可以判斷出該頂點位于頂面。
再次來看骰子的圖形。例如,2點對面是5點,所以頂點6對應(yīng)頂點2,頂點5對應(yīng)頂點1,頂點4對應(yīng)頂點0,頂點7對應(yīng)頂點3。屆時只要逐對比較這些頂點的y值就可以了。

但是,這里還隱藏著一個坑。雖然我們的桌面應(yīng)該是水平的,理論上骰子與桌面接觸面的頂點y值應(yīng)該相同,但是不排除這些頂點之間有細微高度差。即使在小數(shù)點后很多位有差異,在判斷時也會認為這些值不等,從而影響我們的判斷結(jié)果。仍以上圖中的骰子為例。如果頂點0和頂點1的y值略高于頂點2和頂點3的y值,頂點4和頂點5的y值略高于頂點6和頂點7的y值,那么最后可能得出頂面上的骰子點數(shù)為1點或2點。這顯然不是我們想要的結(jié)果。為了得到唯一值,我們可以將y值四舍五入,這樣就能避免上面提到的情況。
最后,將得到的點數(shù)傳給UI,就可以在畫面上顯示出讀數(shù)了。寫好腳本后,記得要把Inspector中的Number Text拖入腳本相應(yīng)欄位。
(五)添加音效
最后,我們還可以添加骰子撞擊桌面的音效。首先準備好音效素材,然后來考慮如何設(shè)置播放音效的條件。撞擊聲必然發(fā)生在骰子與桌面碰撞時,所以可以用OnCollisionEnter來判斷,不過游戲剛開始時,骰子與桌面也有接觸,我們可以用一個定時器來解決這個問題。拋起骰子時開始計時,只有計時數(shù)大于0時,骰子碰撞桌面才會播放音效。