[Unity遊戲作品]Unity初體驗--進階版踩地雷PLAMAI與如何編程


  各位朋友好,小弟很久沒有寫遊戲相關的文章了。這回來聊聊怎麼做個踩地雷的小遊戲,而且稍微不同於以往的玩法喔~(つ´ω`)つ


小遊戲資訊:

遊戲名稱:PLAMAI (源自於プラスル與マイナン)
遊戲類型:益智(PUZ)
遊戲啟發:踩地雷
下載點:https://drive.google.com/file/d/1QCelvMYQGurHx4J1sTu5dByMu2Mjdq7O/
Github開源程序:https://github.com/hcyang1227/Proj


遊戲與Unity截圖:

2022/2/19更新V0.1.0
2022/2/24更新V0.1.1

遊戲內容:

玩法也感覺跟以往的踩地雷很相似:

  1. 若單純只有滑鼠左鍵按下彈起,則點開該格內容物
  2. 若單純只有滑鼠右鍵按下彈起,則切換預測可能為地雷的格子
  3. 滑鼠左鍵與滑鼠右鍵同時按下並且其中一鍵彈起,則確認附近8格預測地雷數與點下數值是否相同,進而開拓其他位置的格子

  比較不一樣的是,地雷有分"正電"拍拍地雷"負電"拍拍地雷,正電會讓周遭格子+1,負電會讓周遭格子-1。這讓遊戲多了許多的變化性,也讓遊戲沒辦法像以往一樣無腦的一直按滑鼠左鍵+滑鼠右鍵拓展地圖,必須思考有沒有陷阱,例如"0"的格子周遭有1個正電地雷跟1個負電地雷,如果直接按滑鼠左鍵+滑鼠右鍵會炸開(作者好壞(*´艸`*))

程式碼:

來聊聊這個踩地雷裡面用到了哪些判定方法跟數值吧。我們首先來看Stage.cs這個source code。
https://github.com/hcyang1227/Proj/blob/main/003_PLAMAI/Assets/Stage.cs


1
uiText.text = "視窗大小:\nWidth=" + Screen.width + ", Height=" + Screen.height + "\n" + uiText.text;

  首先是Screen.width與Screen.height[1]。為了解決更改螢幕大小時造成創建格子(以下稱為Btn)的相對位置跑掉,小弟試著用視窗的長寬跟Btn的長寬比例算出應該要在哪個點創建Btn。可惜這個方法並沒有解決所有的問題,只要在創建Btn後改變視窗的大小,Btn的位置又會跑掉。最後小弟只好用個折衷的方式,一旦創建Btn後改變視窗的大小,就把所有Btn全部destroy掉(淚。゚ヽ(゚´Д`)ノ゚。)。


1
bool StageBoolX = int.TryParse(InputFieldX.text, out StageNumX);

  再來是int.TryParse[2]。由於小弟的程式內用到了InputField輸入欄,欄內可以輸入任何文字,但是小弟希望用的是數字、而非空白或英文或英數交摻的文字。所以用int.TryParse便可很輕易地判別輸入的是否為數字,後續也可以用函式吐出的StageBoolX與StageNumX來做後續的判定(例如若StageBoolX為false,代表輸入的不是數字,這時候就可以後續自訂要帶入什麼預設值)。


1
2
3
4
GameObject Clone;
Clone = (GameObject)Instantiate(Btn, myVector, new Quaternion(), BtnZone.transform);
Clone.GetComponent<Image>().color = Color.gray;
Clone.name = "Btn" + i + "-" + j;

  接下來是Instantiate[3]。Instantiate就是實例化的意思,當你想要從參考物件生成一個物件的時候,便可以用這個寫法。在生成Btn物件並設定好其位置與旋轉角度後,接下來是為Btn著色,這時我們還會用到泛型的用法<XXX>(),GetComponent可以去抓取該物件的一些屬性,例如Btn裡面有Image,其中有color這個項目,便可以套用灰色Color.gray在Btn身上。Btn名稱的部分也可以修改,以這個例子來說,Clone.name便是這個Btn原本的名字,可以套入一些變數後續修改成如Btn3-5之類的名稱。


  背景的array陣列裡也存了不少程式中會用到的數據,其中小弟用了4個array來存放各式各樣的數據。sgmine:stage mine,1:正電地雷 -1:負電地雷。sgnum:stage number,9:正電地雷 -9:負電地雷 0:附近沒地雷 其他:附近有幾個地雷(正負相消)。sgmap:stage map,-2:預測為負電地雷 -1:預測為正電地雷 0:未開 1:剛踩探測是否為0 2:確認為0、其他數字或地雷。sgopnbtn:stage open button,記憶可以開啟的複數位置,0:沒有 1:有。(,,・ω・,,)


1
if (stagephase == 1 && InputFieldMinePlus.interactable) {InputFieldMinePlus.interactable = false;}

  這個小遊戲中也用到了將按紐、輸入欄功能凍結的語法interactable。使用方法也很直觀,只要在要凍結功能的物件後面加上.interactable,並賦予false的數值,就可以凍結按紐或是輸入欄的功能嘍~(ノ>ω<)ノ


1
uiText.text = "<color=green>踩開</color>了 <color=yellow>(" + SelectX + "," + SelectY + ")</color>\n" + uiText.text;

  Text的內容可以用<color=XXX>著色,也可以透過\n換行[5]。這個功能很方便,可以讓特定的文字顏色改變,也可以在特定的地方換行。不過要注意的是,在C#編輯器中\n是有用的,但要在文本框內使用換行符號,就要輸入\\n才會作用喔~(๑´ㅂ`๑)


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
System.Random rand = new System.Random(Guid.NewGuid().GetHashCode());
while(!BtnEmpty)
{
    RndX = rand.Next(0,StageNumX);
    RndY = rand.Next(0,StageNumY);
    if (sgmine[RndX,RndY] == 0 && SelectX != RndX && SelectY != RndY)
    {
        sgmine[RndX,RndY] = MineType;
        BtnEmpty = true;
    }
}

  接著要討論的是亂數System.Random[6]。亂數在所有遊戲程式中是非常常用到的一個數值。在unity當中,也可以使用Random.Range(0, 100)之類的方式來產生亂數,不過這邊小弟還是用C#原生的亂數。在宣告rand為System.Random這個struct以後,使用rand.Next()會傳回一個非負的亂數整數,括號裡面可以填入要產生亂數的最大最小值,如此一來便可在這個區間內產生整數亂數


1
Color BtnColor = new Color(1f, 1f-sgnum[i,j]/8f, 1f-sgnum[i,j]/8f, 1f);

  再來程式裡面也用到了Color這個struct[7]。在Color之後括號內的數值依序為red, green, blue, alpha,數值都在0~1之間。為了讓程式編撰者方便讀懂代碼,一般也會寫成類似128f/255f之類的寫法(因為一般RGB色彩數值常以0~255之間的數值表示,然而在unity當中並非這樣表示)。


1
2
if (Input.GetMouseButtonDown(0)) {MouseDownLeft = true;}
if (Input.GetMouseButtonDown(1)) {MouseDownRight = true;}

  一個unity遊戲中最常見的寫法,應該就屬Input.GetMouseButtonDown了吧[8]。Input.GetMouseButtonDown(0)裡面的0代表滑鼠左鍵,1代表滑鼠右鍵;GetMouseButtonDown代表滑鼠剛按下去的事件,GetMouseButtonUp代表滑鼠剛彈起來的事件。本作品當中判定只按滑鼠左鍵、只按滑鼠右鍵、以及滑鼠左右鍵一起按,就是靠這個函式去辨明的。

  其他程式碼的部分沒有太多艱澀的內容,比較像是腦筋急轉彎之後的成果,所以這邊就不多加贅述嘍~(✪ω✪)

接下來我們再來看BtnHover.cs這個source code。
https://github.com/hcyang1227/Proj/blob/main/003_PLAMAI/Assets/BtnHover.cs


1
2
3
4
5
6
7
8
9
public void OnPointerEnter(PointerEventData eventData)
{
    //從掠過按鈕的名稱擷取Btn位於array的位置
    string str = name;
    str = str.Remove(0,3);
    string[] strAry = str.Split('-');
    Stage.HoverX = int.Parse(strAry[0])-1;
    Stage.HoverY = int.Parse(strAry[1])-1;
}

  首先,我們可以從掠過按鈕的名稱擷取Btn位於array的位置[9]。在OnPointerEnter這個函式中,滑鼠若進入某個Btn領域內,它的名稱會被放到name裡面去,我們便可用處理string字串的方式(例如本作中使用的Remove與Split)來分解出滑鼠滑過某個Btn的陣列X值與Y值。

  另外也要提到的是,這邊用到了static靜態類型[10]的用法,static的引入可以讓原本不能取得stage.cs數值的BtnHover.cs變得可以取得,只要在stage.cs裡面要全域化的變數前面加上static,在BtnHover.cs裡面的變數前面加上Stage.,就可以神奇的使用並流通數值嘍~☆⌒(*^-゜)v

最後我們再來看WindowControl.cs這個source code。
https://github.com/hcyang1227/Proj/blob/main/003_PLAMAI/Assets/WindowControl.cs

  這邊小弟使用了其它大大的成果,達成限制視窗不得小於某個大小[11]。由於是寫好的套裝程式,小弟就直接引用了,詳情請見原作者的Github說明,這邊便不做太多說明了。

小結:

  目前發布的PLAMAI進階版踩地雷是V0.1.0版本,之後如果有更新,也會在這篇下面繼續堆加下去。如果遊戲美化到一個程度,也會考慮上架到steam以及做成手機版本的遊戲喔~對這個小遊戲有什麼想法或是建議等等,也都歡迎在底下留言,感謝~(灬ºωº灬)

參考資料:

[1] 1024搜 - Unity开发过程中如何正确的拿到Screen的width和height
https://www.1024sou.com/article/684971.html

[2] 純淨天空 - C# int.TryParse用法及代碼示例
https://vimsky.com/zh-tw/examples/usage/chash-int-tryparse-method-03.html

[3] 痞客邦 - Unity C# 生成 Instantiate
https://cindyalex.pixnet.net/blog/post/207798662-unity-c%23-%E7%94%9F%E6%88%90-instantiate--%28%E4%B8%80%29%E7%94%9F%E6%88%90%E7%89%A9%E4%BB%B6%E5%9C%A8%E6%9F%90%E7%89%A9%E4%BB%B6

[4] Unity - disable UI button
https://answers.unity.com/questions/1225741/disable-ui-button.html

[5] CSDN - UGUI中Text的自适应换行问题
https://blog.csdn.net/qq_38721111/article/details/80583605

[6] Tim Chang's Blog - [C#] 隨機產生值 v.s. Unity 隨機產生值
https://kw0006667.wordpress.com/2013/04/02/c-%E9%9A%A8%E6%A9%9F%E7%94%A2%E7%94%9F%E5%80%BC-vs-unity-%E9%9A%A8%E6%A9%9F%E7%94%A2%E7%94%9F%E5%80%BC/

[7] Unity Documentation - Color
https://docs.unity3d.com/ScriptReference/Color.html

[8] Unity Documentation - Input.GetMouseButtonDown
https://docs.unity3d.com/ScriptReference/Input.GetMouseButtonDown.html

[9] CSDN - 【Unity3D UGUI】事件接口(一) 鼠标移入、移出
https://blog.csdn.net/eazey_wj/article/details/65947037

[10] CodingNote.cc - Unity的C#編程教程_54_靜態類型 Static Types 詳解及應用練習
https://codingnote.cc/zh-tw/p/233714/

[11] Github - kirevdokimov/Unity-Minimum-Window-Size
https://github.com/kirevdokimov/Unity-Minimum-Window-Size

沒有留言:

張貼留言