從零開始做游戲(六)寫個拋硬幣游戲

之前的項目暫時中斷下,因為UP發(fā)現(xiàn)再寫下去就越來越不是從零了,所以UP考慮來個更容易入門的項目,
起因是這樣的,UP今天偶然的一個這樣那樣的需求總之就是突然說到來拋個硬幣試試,正面就這樣,背面就那樣,然后現(xiàn)在都是掃碼支付時代嘛,找個硬幣都夠折騰的,于是UP找了許多拋硬幣app,不是彈窗廣告就是彈網頁廣告,用戶體驗極差,感覺很不愉快,不過也是都為了掙錢嘛也能理解。
于是UP決定自己動手寫一個非常干凈的簡單的拋硬幣游戲,就把這種簡單的東西分享給大家吧,或許以后有人會感謝我的,對吧。
那么我們就開始動手吧,打開我的Unity,什么居然還是用unity寫,是的UP就對Unity熟悉,所以就不要折騰其他開發(fā)工具了!
主界面與素材
首先建立項目,為項目起名,創(chuàng)建一個2D的項目,這些都挺簡單的就不再描述了,如果想了解UP可是寫過這些文章的哦。
然后我們需要個素材,簡單的畫個線框圖硬幣吧,打開PS,可能你會想,搜索個圖片作為素材不就可以了么,不過這樣你有考慮過版權么,UP可是一直都在強調版權的,所以我們仿著1元硬幣畫了個,也不一樣是吧,只是那個1字特別像,雖然有點丑不過是那個意思吧。

然后導出圖片成PNG格式,不過這里需要注意下圖片正反面的大小一定要保持一致哦。
PS的相關技術各位可以請教下PS大佬,UP根本就是畫圖廢人,所以就不教學這方面了

總之最后我們得到了2張素材

打開UNITY,將圖片拖到場景里

這個工程中,我們修改了背景顏色,至于怎么修改,仔細看上圖,有什么地方顯示出了藍色的字體呢,對啦,就是調節(jié)那個修改的,自己找找吧。
為什么我想讓背景顏色深一點呢,是因為考慮到不要那么瞎眼。
使用Unity代碼來寫按鈕
如果你是UP的忠實觀眾,那么你應該記得UP曾教學過UGUI這個Unity里自帶的可視化UI系統(tǒng),當然如果不是的話也不必去查找那一期啦,不如從現(xiàn)在開始成為UP的忠實觀眾好不好呀。(這個不要臉的UP)
其實在Unity中,UGUI出現(xiàn)以前,還有一個官方的GUI代碼模塊,具體是什么參見下面的代碼。
void OnGUI()
{
//這個意思就是這里面的內容會被認為是GUI模塊
}
在unity中用void聲明的有許多內容,比如Start,Update等等,UP在曾經的教學中也有提到過,所以大家可以回去翻....是的,誰想翻啊,所以UP只能再來一遍并補充一點。
首先需要明白的是,寫Unity的代碼時,要寫在聲明(就是void的那個大括號里面)
void Awake ():在游戲開始前就調用一次
void Start():在Awake后調用一次
void Update():在Start后調用,而且是無限循環(huán)的調用
void OnGUI():在Update后調用,在這里可以寫入GUI代碼,無限循環(huán)的調用
其實Unity里還有許多這樣的聲明,這里僅簡單的說了常用的4種,一定要記得運行的先后順序,因為未來很多報錯的可能性都是先后順序搞反了造成的,所以大家記住順序了嗎,沒有?沒關系,接著往下看,用多了就記住了,不必背誦,背了也沒用。
我們來用代碼寫個按鈕,可能你不太明白具體的意思,復制粘貼就好,不必深入理解。

寫好后拖代碼

然后運行(記得先保存一下,記得隨時存檔啊存檔啊,如果你發(fā)現(xiàn)你的場景名字后面有個*,就像上圖一樣就是你沒有存檔,如果突然停電了系統(tǒng)崩潰了等等等情況發(fā)生你會崩潰的)

你會發(fā)現(xiàn)屏幕里多了個按鈕,以及點擊按鈕后,控制臺會給你一句這樣的提示,這代碼這個按鈕是有用的
對按鈕進行改造
我們成功的將按鈕功能實現(xiàn)了,但是有些問題,比如按鈕的位置好像很奇怪啊,所以我們改一下。
GUI.Button(new Rect(10, 10, 50, 50)
上面這段代碼有4個數(shù)字看到了嗎,其中10,10代表在屏幕的坐標X=10 Y= 10.
后兩位數(shù)字50 50代表的是長X寬
所以我們要把它放在屏幕下面居中,需要什么位置呢,算一下坐標????
你會發(fā)現(xiàn),不同的分辨率下按鈕的位置都不一樣,所以這時候我們需要一個相對固定坐標的算法。
這里有個新知識:Screen.width獲取屏幕寬度,Screen.height獲取屏幕高度,所以UP想怎樣呢。
這里教學一個可能專業(yè)課里也學不到的算法:
通過屏幕寬絕對居中按鈕:屏幕寬度分辨率/2 - 按鈕長度/2
而高度,我們只希望他比屏幕最低的地方往上數(shù)10分辨率為間隙,所以直接用Screen.height - 50,為什么是50,因為我們的按鈕高度是40,想一想有明白嗎。

這個算法怎么來的,常年的經驗,不斷的摸索,有時候也可以由老司機程序員傳授給你,其實并不需要你是數(shù)學家,只需要你經驗豐富。

實際上按鈕的大小也可以根據(jù)分辨率修改,不過UP認為這個項目沒必要做那么復雜,所以就沒有做這個功能了,不然就會開發(fā)時間增加增加再增加,開發(fā)項目的時候,多多思考下效率的問題,即用最少的時間將項目上線。
當然,按鈕的皮膚也可以修改,不過在一個項目根本就不知道有多少人用的情況下,這些就暫時不改了,如果發(fā)現(xiàn)莫名的使用的人還很多,UP就會去繼續(xù)增加新功能,好像UP把這么多年總結的產品制作經驗也順帶告知大家了。
實現(xiàn)后端功能
拋硬幣的功能是怎么實現(xiàn)的,其實是這樣的:
首先硬幣的正反實際上是在后端用隨機2個數(shù)字來展示的,0是背面,1是正面,而硬幣的圖像其實是隨著結果(0或1數(shù)字)所顯示的部分,其中數(shù)字0、1叫后端,圖片顯示叫前端。
(順帶一說,實際上現(xiàn)在網上的抽獎都是這樣的,在你點擊的那么一瞬間,后端就給出了所代表獎品的數(shù)字,而那個表盤轉啊轉只是給你看的假象而已,只是它們的后端是在服務器里,如果不在服務器你就可以用修改器作弊了)
這個隨機數(shù)生成的代碼非常的簡單,不過需要注意的是,大家看注釋里寫的是0-1這2個隨機數(shù),而我所填寫的是0,2,如果這里寫0,1,你永遠只能抽到0了。
Random.Range(0,2)就是生成隨機數(shù)0-1,如果想生成0-2需要寫成什么呢:
Random.Range(0,3);
而print(目的是為了讓其結果打印在控制臺里,讓我可以測試并看見)


前端的表現(xiàn)
實際上到剛才那里功能已經實現(xiàn)一大半了,接下來要做的就是用圖像顯示真反面,實現(xiàn)原理是這樣:
通過隨機數(shù)0或1生成圖片,如果0生成背面,1生成正面。
首先我們需要想個辦法讓程序把圖片記住,這個辦法UP已經幫你想好了

關于命名類型實際上有許多知識,比如INT,String,F(xiàn)loat等等,其實各位大可不必在此時了解,也不必背誦。
這里使用的GameObject就是可以存儲游戲組件的類型,總之用來存素材是沒問題啦。
為什么需要存素材,你暫時別想這個問題,看到UP把功能寫好了后,你返回來看肯定能懂。
接下來UP把圖片1,2都導入到了Unity場景中,并把它們移到了屏幕外面,這樣的作用就是你看不見它了。

為了安全起見,UP把它們的坐標設置成了9999,我相信這么遠的距離你用4K顯示器全屏也不會露餡,然后當隨機數(shù)為0的時候,我們就把圖片一的坐標改成X=0 Y=0就可以了。
其實要實現(xiàn)出現(xiàn)硬幣的方法有許多,如果要老老實實的寫,還要寫成加載圖片之類的,這里UP選擇了一種最懶的辦法,總之用戶只管用起來有沒有問題,沒誰管你是怎么實現(xiàn)的,他又不知道你是把硬幣隱藏到屏幕外面了對吧。
然后我們把1和2拖入到設定的GameObject里。

這里就需要注意了,public GameObject前面的public 一定要寫,否則你會找不到該拖哪,不信你照著UP的做法試驗下。
接下來,我寫了個代碼:int i;
這個的意思是,我要用一個字母i來存儲數(shù)據(jù),為什么是i,其實你用a,b,c,d都可以,只是很多程序員都習慣用i來存儲數(shù)據(jù),大概是用愛來存儲數(shù)據(jù)吧,UP初學程序的時候就發(fā)現(xiàn)他們都這樣,于是UP也就這樣了,一種習慣,如果有大佬知道原因還請告之。

至于為什么要用個i來存數(shù)據(jù),因為不存的話你就無法用if代碼來判斷,看下面的代碼。

上面的代碼看懂了嗎?
首先你需要明白一個知識,if(如果),可以說寫程序100%會用到的功能
if (i == 0) 意思就是如果 i = 0,不過這里一定要記住,在程序里“等于”,要2個等號==,寫一個會報錯,總之記住在if里判斷i是否等于0要用 == 。
上圖代碼里,有一段叫做: i = Random.Range(0, 2),在程序里一個等號=叫做賦值;
這里的意思就是把0-1的隨機數(shù)結果賦值給i,所以用了一個等號=。
經歷了這個賦值的操作后,i就變成0或者1了(這個要根據(jù)隨機數(shù)隨機賦予),所以下面if(i == 0)就是判斷i現(xiàn)在等于不等于0啊。
如果等于,執(zhí)行下面一行內容,如果不等于,在看看else里面的內容。
在else里,UP又寫了一段if (i == 1),在i不等于0的時候再判斷下i是不是等于1啊。
實際上else后面可以不用再加if (i == 1)的,因為隨機數(shù)結果只有0或者1嘛,按理說else里的內容就是前面的if (i == 0) 不滿足,就強行執(zhí)行else后面的東西了。
在學校里,老師肯定不會講到這個,如果代碼寫的專業(yè)點的話,一定要在else后面寫上if (i == 1),因為你不知道程序會不會出BUG,多寫一點避免BUG。
以上對于初學程序的你來說會復雜么,或者會不會沒有講的很清楚,UP也不得而知,不過UP自己能看懂就是了。
UP好好總結以及補充上面的教學:
=等號是賦值,== 雙等號才叫等于
if(條件) .A. else .B. 是寫代碼100%會用到的一種方法,意思是 如果滿足if(括號里的條件),執(zhí)行A,否則執(zhí)行B。
當然也可以if(條件A) .C. else if(條件B) .D,意思是如果條件A滿足執(zhí)行C,不滿足再看看條件B滿足嗎,滿足執(zhí)行D,還不滿足,這段代碼不執(zhí)行。
條件A可以為1行或者多行代碼,1行就寫在if代碼的下一行,多行需要添加{大括號把多行代碼括起來,
大括號中間可以回車,分號加在大括號中間;}
而代碼中id1.transform.position = new Vector2(0, 0);的意思就是讓id1的坐標變成X=0 Y=0,為什么要這樣寫,不必思考,反正你知道這樣寫可以實現(xiàn)這個功能就OK了,你把它復制過來就可以。

接下來開始測試與排除BUG,其實肯定有許多問題和優(yōu)化的
首先我們運行下程序,發(fā)現(xiàn)原本我們把代碼調整到屏幕外面去了,程序一運行正面的圖片就自動進入了屏幕中間(這里就不用圖展示了,相信你能懂)
實際上問題發(fā)生的原因是,程序一開始i就自動賦予的0這個值,所以我們做一些修改。
int i = -1; 我們將程序頂部的i初始賦予了-1的值,這樣運行程序的時候屏幕中間什么也沒有。

我們點擊拋硬幣按鈕,會發(fā)現(xiàn)某個硬幣在屏幕里顯示了(實際上大家都知道只是修改了那個圖片的坐標,把坐標改成0了而已)

接下來我們多點了幾下,發(fā)現(xiàn)結果和上圖一樣,出現(xiàn)硬幣永遠都是最初出現(xiàn)的第一枚。
那是因為程序功能還沒有寫完,出現(xiàn)了BUG,這時候就需要找問題修改問題了,這種模式叫做DEBUG,排除BUG,好的程序員,找到BUG和排除BUG的效率絕對高。

修改了下代碼,UP沒有在代碼里寫任何注釋,你試試可以讀懂每一句是什么意思嗎。
如果能讀懂,哦恭喜你程序入門了!
你那知道之前的BUG造成的原因是什么嗎,是因為UP只寫了讓硬幣坐標變成0,忽略了再把不用的硬幣扔出屏幕外的代碼,所以造成圖片重疊了(這個你都發(fā)現(xiàn)了,看來你很有寫代碼的天賦)
好了,這個程序寫完了!

*下面內容有難度, 不建議初學者學習,否則你會懵逼甚至從開始到放棄,所以下面這段內容類似于游戲中的hard模式關卡,只要求部分有能力的玩家玩通,普通玩家看不懂也沒什么問題。
這個功能就寫完了,但是UP覺得有一個小問題會非常影響用戶體驗,即是如果連續(xù)點擊“拋硬幣”按鈕,連續(xù)出現(xiàn)同一個面,你還以為按鈕死機了,所以我們需要優(yōu)化一下這個問題,這個不是BUG,是用戶體驗問題,在初次成型的項目里,這種嚴重影響用戶體驗的地方還是需要修改一下的。
我們可以用程序做一段小動畫:

這段動畫是通過改變圖像的x軸大小來實現(xiàn)的,每次點擊后,程序會按照0.01秒計算一次的方式將x從-2變化到2,所以UP也不再多解釋這一段了,確實是初學程序時搞不定的地方。
后來UP發(fā)現(xiàn)了個問題,拋硬幣按鈕在高分辨率手機上太小,所以我們改了下按鈕的大小代碼,改成這樣了,你能看懂意思嗎,如果可以,恭喜你有天賦,加油學!
GUI.Button(new Rect(Screen.width / 2 - (Screen.width - 10) /2, Screen.height - (Screen.height * 0.15f + 10), Screen.width - 10, Screen.height * 0.15f)
后記
好了,通過一篇文章介紹了一個非常簡單的游戲是如何完成的,雖然這個“游戲”某方面來說只是個工具,不過卻可以通過這個簡單的項目教會大家如何制作完整的項目。
unity做游戲有個最大的缺點是,打包出來的安卓比較大,這個游戲居然20多M,UP還以為500K就搞定了呢,如果大家想看一看這個項目可以訪問pan.baidu.com/s/1dw1HVK下載安卓版,所以B站什么時候推出網絡硬盤呢。
寫完這篇文章的時候已經半夜1點多了...