Unity游戏制作(一)
实验内容
- 编程实践:井字棋
实验环境
-
Windows
-
Unity 2020.3.18
技术日记
一、ECS框架
ECS,即 Entity-Component-System(实体-组件-系统) 的缩写,其模式遵循组合优于继承原则,游戏内的每一个基本单元都是一个实体,每个实体又由一个或多个组件构成,每个组件仅仅包含代表其特性的数据(即在组件中没有任何方法)。
一个使用ECS架构开发的游戏基本结构如下图所示:
二、实验部分
1.使用说明
新建tictactoe文件,然后直接把tictactoe.cs
文件挂在主摄像头main camera即可。
2. 代码注释
本次实验,共设置了设置了七个函数,其功能分别如下
- ResetGame() :初始化游戏参数
void ResetGame(){
playersTurn = false;
firstTurn = true;
board = new string[rows, cols];
playerScore = new int[rows + cols + 2];
computerScore = new int[rows + cols + 2];
winner = null;
moves = 0;
}
将playersTurn
设置为非玩家的回合、fisrtTurn
设置为true,即启动游戏时为第一回合。初始化board
,玩家分数playerScore
和电脑分数computerScore
,并把winner
定义为空,moves
移动步数定义为0。
- Start():启动游戏
void Start(){
ResetGame();
delta = new Vector2Int[4] { new Vector2Int(-1, 0), new Vector2Int(0, -1), new Vector2Int(1, 0), new Vector2Int(0, 1) };
screenCenterX = Screen.width / 2;
screenCenterY = Screen.height / 2;
boardWidth = rows * gridWidth + (rows - 1) * gridMargin;
boardHeight = cols * gridHeight + (cols - 1) * gridMargin;
boardX = screenCenterX - boardWidth / 2;
boardY = screenCenterY - boardHeight / 2;
}
首先调用ResetGame()
函数初始化参数,然后设置好board
和screenCenter
的位置
- InRange():判断行、列是否在正确的范围内
bool InRange(int row, int col){
return row >= 0 && row < rows && col >= 0 && col < cols;
}
定义了const int rows = 3
,const int cols = 3
,因此此函数作用即判断行、列是否在正确的范围内。
- AddScore():判断能否增加分数,如果可以的话记录分数增加。
void AddScore(int[] score, int row, int col){
if (row == col){
score[rows + cols]++;
}
if (row + col == rows - 1){
score[rows + cols + 1]++;
}
score[row]++;
score[rows + col]++;
moves++;
CheckBoard();
}
- GameBoard():控制游戏面板,即主要程序,控制玩家和电脑的落子。
void GameBoard(){
float boardWidth = rows * gridWidth + (rows - 1) * gridMargin;
float boardHeight = cols * gridHeight + (cols - 1) * gridMargin;
float boardX = screenCenterX - boardWidth / 2;
float boardY = screenCenterY - boardHeight / 2;
if(!playersTurn && winner == null){
int row = -1, col = -1;
if(firstTurn){
row = Random.Range(0, rows);
col = Random.Range(0, cols);
firstTurn = false;
}else{
for(int i = 0; i < delta.Length; i++){
Vector2Int tmp = delta[i];
int r = Random.Range(i, delta.Length);
delta[i] = delta[r];
delta[r] = tmp;
}
for(int i = 0; i < delta.Length; ++i){
Vector2Int tryMove = delta[i] + playerLastMove;
int tryRow = tryMove.x;
int tryCol = tryMove.y;
if(InRange(tryRow, tryCol) && board[tryRow, tryCol] == null){
row = tryRow;
col = tryCol;
break;
}
}
if (row == -1 && col == -1){
for(int r = 0; r < rows; ++r){
for(int c = 0; c < cols; ++c){
if(board[r, c] == null)
{
row = r;
col = c;
break;
}
}
}
}
}
playersTurn = !playersTurn;
board[row, col] = "O";
AddScore(computerScore, row, col);
}
for(int row = 0; row < 3; ++row){
for(int col = 0; col < 3; ++col){
float gridX = boardX + row * (gridWidth + gridMargin);
float gridY = boardY + col * (gridHeight + gridMargin);
bool clicked = GUI.Button(new Rect(gridX, gridY, gridWidth, gridHeight), board[row, col]);
if(clicked && board[row, col] == null && playersTurn && winner == null){
board[row, col] = "X";
AddScore(playerScore, row, col);
playerLastMove = new Vector2Int(row, col);
playersTurn = !playersTurn;
}
}
}
}
- Controls():控制标签的位置,给出提示信息。
void Controls(){
float controlWidth = boardWidth;
float controlHeight = gridHeight;
float statusTextX = boardX;
float statusTextY = boardY - (gridHeight + gridMargin);
float resetButtonX = boardX;
float resetButtonY = boardY + rows * (gridHeight + gridMargin);
if(GUI.Button(new Rect(resetButtonX, resetButtonY, controlWidth, controlHeight), "Restart")) {
ResetGame();
}
string message;
if(winner == null){
message = playersTurn ? "Your turn" : "Computer's turn";
}else{
if(winner == "You"){
message = winner + " win!";
}else{
message = winner + " wins!";
}
}
GUI.Label(new Rect(statusTextX, statusTextY, controlWidth, controlHeight), message);
}
游戏的标签部分,显示了“Restart”按钮和游戏信息message
。
- CheckBoard():判断是玩家赢还是电脑赢、还是打成平手
void CheckBoard(){
for(int i = 0; i < playerScore.Length; ++i){
if(playerScore[i] == rows){
winner = "You";
}
if (computerScore[i] == rows){
winner = "Computer";
}
}
if (moves == rows * cols && winner == null){
winner = "Nobody";
}
}
因为rows是个常量为3,因此当玩家的分数为3,则玩家获胜,否则电脑获胜,如果总步数达到了九步,且分不出胜负,则打成平手。
- OnGUI() :游戏场景渲染之后调用的函数
void OnGUI(){
GUI.skin = skin;
GameBoard();
Controls();
}
实验总结
在完成本次实验中,一开始对井字棋游戏的代码编写没有思路,以为是通过防止2D方块完成。通过查找资料后,发现是通过写代码以及UI界面,并直接把.cs
文件挂到主摄像头去,因此才完成代码的编写。