Unity3D井字棋小游戲
一、 一些小問題
由于這是第一次作業,業務不太熟悉,在完成的程序中遇到了一些問題,在此記錄下來,
-
OnGUI()函式是每隔一段時間自動執行的,因此盡量在避免在該函式中出現次數過多的回圈甚至死回圈,這樣可能會導致游戲無法運行,甚至整個Unity程式無法回應,最后只能任務管理器強行關閉...
-
Game視角和整個游戲build and run之后的視角大小是不一樣的,比如ctrl+B之后運行的游戲視角是正常的,但在點擊運行按鈕在程式中看到的視角是不全的,

-
一開始想要找一些圖片作為游戲背景,但發現無論怎么調節圖片的大小,始終無法鋪滿整個游戲界面,甚至當調到一定大小之后再往大調圖片不會再有變化了,后來發現這是與圖片本身的大小有關,因此在選擇圖片的時候還需要考慮其自身的大小,
二、 實作程序和方法
1. 設計思路
-
根據ECS架構,需要將代碼內容分為物體、組件和系統三部分,
-
物體指玩家、棋盤、界面
-
組件層面,玩家由玩家資訊,如turn等資料組成;棋盤由二維陣列中的值即棋子組件組成;界面則由模式mode、isStart等資料組成,
-
系統主要分為
-
GUI渲染系統:對棋盤的資料以及當前玩家對戰資訊進行可視化,從而讓玩家看到當前的棋盤內容以及對局形勢,
-
玩家系統:通過接受來自玩家的輸入,將操作傳遞給棋盤,改變棋盤的資料,從而實作玩家下棋,
-
游戲整體系統:實作改變玩家的操作狀態、選擇游戲模式、判定游戲形勢等功能,
-
2. 具體函式及資料
不同系統之間要實作低耦合,故不同系統之間只留有幾個介面,如GUI渲染系統則主要是利用OnGUI()函式對游戲界面進行渲染,在OnGUI()函式中,將前端收到的訊息傳遞給后端玩家系統或游戲整體系統,具體的表現為當按鈕被按下時呼叫玩家系統或游戲整體系統相應的函式,因此,OnGUI()函式中除了界面的渲染以外,其它基本都是函式的呼叫,
(1)資料部分
定義的資料如下,三個共有變數接受傳入的圖片,在OnGUI函式中對界面起到裝飾效果,isStart變數標志游戲的狀態,mode變數標志游戲模式,board二維陣列是棋盤的模擬,turn表示當前輪到哪位玩家下棋,
public Texture2D point;
public Texture2D victory;
public Texture2D balance;
?
bool isStart;
int mode;// 0-單人模式 1-雙人模式
int [,]board = new int[3,3];//0-無棋子 1-O棋子,2-X棋子
int turn;// 0-player1 1-player2
int chooseTurn = 0;//0-當前無需選擇,1-當前需選擇玩家輪次
(2)GUI渲染系統
OnGUI()函式
主要分為三部分:
-
按鈕的監聽
-
棋盤的渲染
-
游戲提示的渲染
void OnGUI(){
GUIStyle labelStyle = new GUIStyle();
GUIStyle titleStyle = new GUIStyle();
GUIStyle chessStyle = new GUIStyle();
?
labelStyle.fontSize = 30;
titleStyle.fontSize = 100;
chessStyle.fontSize = 80;
chessStyle.alignment = TextAnchor.MiddleCenter;
?
GUI.Label(new Rect (680, 10, 600, 150),"Tic Tac Toe",titleStyle);
if(isStart){
if(mode == 0 && chooseTurn == 0){
GUI.Label(new Rect (80, 80, 600, 150),"單人模式游戲中...",labelStyle);
singleMode();// 呼叫單人模式函式
}
if(mode == 1){
GUI.Label(new Rect (80, 80, 600, 150),"雙人模式游戲中...",labelStyle);
doubleMode();// 呼叫雙人模式函式
GUI.Label(new Rect (250, 680, 600, 150),"輪到玩家1下棋",labelStyle);
GUI.Label(new Rect (250, 840, 600, 150),"輪到玩家2下棋",labelStyle);
// 玩家輪次切換提示
if(turn == 0){
GUI.Label(new Rect (0, 650, 200, 150),point);
}
else{
GUI.Label(new Rect (0, 800, 200, 150),point);
}
?
}
?
}
else{
GUI.Label(new Rect (80, 80, 600, 150),"請選擇模式...",labelStyle);
}
// 若點擊單人模式,則通過設定mode變數進入單人模式,并將游戲狀態置為開始
if(GUI.Button(new Rect (80, 200, 200,100), "單人模式")){
chooseTurn = 1;
if(!isStart){
mode = 0;
isStart = true;
}
}
// 單人模式玩家選擇先后或后手
if(chooseTurn == 1){
if(GUI.Button(new Rect(50, 680, 300, 150),"先手")){
turn = 0;
chooseTurn = 0;
}
if(GUI.Button(new Rect(50, 840, 300, 150),"后手")){
turn = 1;
chooseTurn = 0;
}
}
// 若點擊單人模式,則通過設定mode變數進入雙人模式,并將游戲狀態置為開始
if(GUI.Button(new Rect (80, 350,200,100), "雙人模式")){
if(!isStart){
mode = 1;
isStart = true;
}
}
// 渲染棋盤
for (int i=0; i<3; ++i) {
for (int j=0; j<3; ++j) {
if (board [i, j] == 1){
GUI.Button (new Rect (580 + i * 250, 220 + j * 250, 250, 250),"O",chessStyle);
}
if (board [i, j] == 2){
GUI.Button (new Rect (580 + i * 250, 220 + j * 250, 250, 250),"X",chessStyle);
}
}
}
// 渲染游戲結果
switch (isEnd()){
case 0:
break;
case 1:
GUI.Label(new Rect (1500, 150, 600, 150),"先手玩家獲勝!",labelStyle);
GUI.Label(new Rect(1400,200,600,550),victory);
break;
case 2:
GUI.Label(new Rect (1500, 150, 600, 150),"后手玩家獲勝!",labelStyle);
GUI.Label(new Rect(1400,200,600,550),victory);
break;
case 3:
GUI.Label(new Rect (1500, 150, 600, 150),"平局",labelStyle);
GUI.Label(new Rect(1400,200,600,550),balance);
break;
default:
break;
}
// 若點擊重新開始,則清空棋盤并重置游戲資料
if(GUI.Button (new Rect (80, 500, 200, 100),"重新開始")){
reset();
}
?
?
}
(3)玩家系統
-
putChess()
通過接受來自OnGUI函式傳遞過來的資料,即棋盤的具體坐標(i,j),修改棋盤模型board[i,j]的值而實作下棋的功能,若游戲還未結束,且輪到當前玩家且棋盤(i,j)上尚未有棋,則可以成功下棋,
bool putChess(int i,int j){
if(isEnd() != 0){
return false;
}
if (board[i,j] == 0) {
if (turn == 0){
board [i, j] = 1;
}
else{
board [i, j] = 2;
}
return true;
}
return false;
}
-
randomPut()
此函式是用于電腦玩家的下棋,實作只是通過簡單的不斷生成隨機坐標并嘗試該坐標能否下棋,能則下棋,否則則繼續隨機嘗試,
void randomPut(){
int i;
int j;
while(true){
i = (int)Random.Range(0,3);
j = (int)Random.Range(0,3);
if(board[i,j]==0){
putChess(i,j);
changeTurn();
break;
}
}
}
(4)游戲整體系統
-
Start()
游戲專案開始運行時的對游戲資料進行初始化,實際上就是呼叫reset()函式
void Start(){ reset(); } -
reset()
重置游戲資料,包括棋盤資料,游戲狀態,玩家輪次
void reset(){ for(int i = 0;i < 3;++i){ for(int j = 0;j < 3;++j){ board[i,j] = 0; } } turn = 0; isStart = false; } -
isEnd()
判斷通過判斷代表棋盤的二維陣列中的資料來判斷當前游戲形勢,
// 0 - 還未結束 // 1 - player1 win // 2 - player2 win // 3 - 平局 int isEnd(){ int count = 0; for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (board [i, j] != 0) count++; } } // 一行 for (int i=0; i<3; ++i) { if (board[i,0]!=0 && board[i,0]==board[i,1] && board[i,1]==board[i,2]) { return board[i,0]; } } // 一列 for (int j=0; j<3; ++j) { if (board[0,j]!=0 && board[0,j]==board[1,j] && board[1,j]==board[2,j]) { return board[0,j]; } } // 對角線 if (board[1,1]!=0 && board[0,0]==board[1,1] && board[1,1]==board[2,2] || board[0,2]==board[1,1] && board[1,1]==board[2,0]) { return board[1,1]; } if (count == 9){ return 3; } return 0; } -
changeTurn()
修改玩家輪次
void changeTurn(){ turn = 1 - turn; } -
singleMode()
單人模式下的游戲邏輯,若輪到真人玩家,則等到真人玩家點擊棋盤下棋,否則由randomPut()函式代表電腦玩家隨機下棋,
void singleMode(){ for (int i=0; i<3; ++i) { for (int j=0; j<3; ++j) { if (GUI.Button (new Rect (580 + i * 250, 220 + j * 250, 250, 250),"")) { if(turn == 0){ if(putChess(i,j)){ changeTurn(); } } } ? } } if(turn == 1 && isEnd() == 0){ randomPut(); } } -
doubleMode()
雙人模式下的游戲邏輯,當前玩家下完棋之后改變輪次由另一位玩家下棋,
void doubleMode(){ for (int i=0; i<3; ++i) { for (int j=0; j<3; ++j) { if (GUI.Button (new Rect (580 + i * 250, 220 + j * 250, 250, 250),"")) { if(putChess(i,j)){ changeTurn(); } } } } }
三、效果展示
視頻鏈接
四、專案地址
huanghj78/Unity-Homework - 碼云 - 開源中國 (gitee.com)
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/301844.html
標籤:其他
上一篇:【歷史上的今天】9 月 20 日:中國正式接觸互聯網;抖音上線;中科大成立
下一篇:devc++未成年保護模式系統
