IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> 中山大学软件工程-Unity井字棋作业 -> 正文阅读

[游戏开发]中山大学软件工程-Unity井字棋作业

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)

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2021-09-22 14:59:26  更:2021-09-22 15:00:00 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/28 4:51:19-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码