1 要求:

  • 游戏内容: 井字棋 或 贷款计算器 或 简单计算器 等等
  • 技术限制: 仅允许使用 IMGUI 构建 UI
  • 作业目的:
    • 了解 OnGUI() 事件,提升 debug 能力
    • 提升阅读 API 文档能力

2 资源地址

3 界面图

3.1 界面设计:

3.2 界面展示:

开始菜单

单人模式

双人模式

4 配置和运行说明

4.1 建立场景

建立3个场景分别为StartMenu(开始菜单),singlePlayGame(单人游戏),doublePlayGame(双人游戏)

4.2 设置场景对应的运行脚本

分别在每个场景的文件目录下建立空的Gameobject,创建3个c#脚本文件,分别命名为StartMenu(开始菜单),singlePlayGame(单人游戏),doublePlayGame(双人游戏),并将3个脚本文件分别拉到对应场景(同名场景)目录下的Gameobject中。

4.3 设置背景

脚本中定义已经好背景img,场景下的Gameobject详情页会背景详情如下:

将选好的背景图片拉到Img方框处即可设置背景

4.3 定义和配置皮肤

皮肤的设置同理

按照下图找到GUI Skin对象并点击创建,将该对象拉去到上图Skin方框处即可设置皮肤

点击资源中的皮肤(以下是我的设置,可以随意挑选自己喜欢的风格)

点击button其下的normal选项:设置其Background参数为Background款式,按钮文字颜色设置为黑色。

设置button下的border选项的Bottom参数为1,这样按钮就会产生一定的阴影效果

设置button下的border选项的Overflow菜单下的Font Size参数值为30,调整棋盘中X,O图标的大小。

5 算法思路

5.1 开始菜单:

设计标题,按钮,背景图片,字体,皮肤的风格和位置,按照 IMGUI 官网指导编写脚本,设计按下单人游戏按钮切换到单人游戏界面,设计按下双人游戏按钮切换到双人游戏界面。

5.2 单人游戏:

设计九宫格,返回菜单按钮,重新开始按钮位置背景图片,字体,皮肤的风格和位置,按照 IMGUI 官网指导编写脚本,在OnGUI()函数中编写下棋界面,OnGUI()快速刷新展示页面。

在单人模式下,设置玩家先下棋子,定义turn等于玩家,当玩家点击按钮后,该位置显示“X",并将turn转给PC,电脑按照以下的最优算法寻找下棋位置:

  1. 先优先搜索位置,使得电脑下完该棋后即可胜利;(搜索斜行,横行,纵行,找到一行,满足有一个空格,两个PC占有的格子);
  2. 如果没有找到满足以上要求的格子,再搜索位置,使得电脑占有该格后玩家不能够在下一步取得胜利;(搜索斜行,横行,纵行,找到一行,满足有一个空格,两个玩家占有的格子);
  3. 如果没有找到满足以上要求的格子,再随机选择空格,返回。
  4. 没有空格则直接返回。

当PC端下完该步棋后,该位置显示“O",turn转为玩家,由玩家下棋,双方轮流下棋,直到棋盘满了或者有一人胜利。

在OnGUI()函数实现ShowResult(Check())函数来不断的检查是否有胜利者产生,如果有,打印胜利消息并结束该局。

5.3 双人游戏:

设计九宫格,返回菜单按钮,重新开始按钮位置背景图片,字体,皮肤的风格和位置,按照 IMGUI 官网指导编写脚本,在OnGUI()函数中编写下棋界面,OnGUI()快速刷新展示页面。

在双人模式下,设置玩家1先下棋子,定义turn等于玩家1,当玩家1点击按钮后,该位置显示“X",并将turn转给玩家2,当玩家2点击按钮后,该位置显示“O",turn又转为玩家1,由玩家1下棋,双方轮流下棋,直到棋盘满了或者有一人胜利。

在OnGUI()函数实现ShowResult(Check())函数来不断的检查是否有胜利者产生,如果有,打印胜利消息并结束该局。

6 具体功能实现:

6.1 开始界面:

定义全局变量:

    public Texture2D img;//背景图片public GUISkin skin;//界面皮肤

定义位置参数:

        //定义数值参数float height = Screen.height * 0.5f;float width = Screen.width * 0.5f;int ButtonHeight = 50;int ButtonWidth = 150;int TitleHeight = 80;int TitleWidth = 200;

Screen.height 和 Screen.width 为当前窗口的高和宽,利用这两个参数,找到界面的中心位置height,width,其他参数ButtonHeight,ButtonWidth是用来表示按钮大小,TitleHeight,TitleWidth表示标题大小。

GUI添加控件的构造函数的参数有一个 GUIStyle 类型的参数,用来设置两个标题的样式,背景图片的样式和按钮字体的样式

        //定义字体风格GUIStyle tStyle1 = new GUIStyle {fontSize = 40,fontStyle = FontStyle.Bold,};GUIStyle tStyle2 = new GUIStyle {fontSize = 30,fontStyle = FontStyle.Bold,};//设置背景图片GUIStyle BackgroundStyle = new GUIStyle();BackgroundStyle.normal.background = img;//设置按钮字体风格GUIStyle ButtonStyle = new GUIStyle("button");ButtonStyle.fontSize = 20;

居中放置标题位置:

       //放置标题GUI.Label(new Rect(0, 0, Screen.width, Screen.height), "", BackgroundStyle);GUI.Label(new Rect(width - TiGUI.Label(new Rect(width - TitleWidth / 3, height - TitleHeight * 2, TitleWidth, TitleHeight), "井字棋", tStyle1);GUI.Label(new Rect(width - TitleWidth / 3, height - TitleHeight , TitleWidthtleWidth / 3, height - TitleHeight * 2, TitleWidth, TitleHeight), "井字棋", tStyle1);GUI.Label(new Rect(width - TitleWidth / 3, height - TitleHeight , TitleWidth, TitleHeight), "开始菜单", tStyle2);

设置按钮位置,设置按下按钮后,调用 SceneManager.LoadScene函数实现界面跳转,其中该函数的第二个参数为LoadSceneMode.Single,表示跳转后销毁当前界面,实现如下:

        //放置模式选择按钮if (GUI.Button(new Rect(width - ButtonWidth / 2 - 100, height + ButtonHeight / 4, ButtonWidth, ButtonHeight), "单人模式", ButtonStyle)) {SceneManager.LoadScene("singlePlayerGame", LoadSceneMode.Single);//切换到"OnePlayerMode"界面并销毁开始菜单界面}if (GUI.Button(new Rect(width - ButtonWidth / 2 + 100, height + ButtonHeight / 4, ButtonWidth, ButtonHeight), "双人模式", ButtonStyle)) {SceneManager.LoadScene("doublePlayerGame", LoadSceneMode.Single);;//切换到"TwoPlayersMode"界面并销毁开始菜单界面}

6.2 单人游戏界面:

界面的组件有结果消息,九宫格,返回菜单按钮,重新开始按钮组成,通过设置这些组件的左上角坐标来将他们放置在合适的界面位置:

    //一些坐标位置int backButtonX;int backButtonY;int firstGridX;int firstGridY;int resetButtonX;int resetButtonY;int resultMsgX;int resultMsgY;

棋盘逻辑表示使用int变量,其中empty表示空的,you表示你下的,pc表示电脑下的,都用于填在checkerboard中、

    private int [,] checkerboard=new int[3,3];//棋盘private const int empty=0;private const int you=1;private const int pc=2;

全局变量布尔变量playing表示游戏是否正在进行,进行为true,不进行为false,整数变量turn用来指定当前轮轮到谁下棋,可以在单人游戏中可以为上面代码定义的you或者pc。

OnGUi函数代码以及详细注释如下,界面组件的位置根据当前界面的高度,宽度和按钮大小设置,保证组件的居中,该函数设置字体风格,背景,以及放置组件位置,每一次刷新都调用ShowResult(Check());函数检查是否有胜利者。

        GUI.skin=skin;//设置皮肤//界面参数设置backButtonX=Screen.width / 2 -buttonWidth/2-buttonWidth;backButtonY=Screen.height / 2 +buttonWidth+buttonWidth/2;firstGridX=Screen.width / 2 -buttonWidth/2-buttonWidth;firstGridY=Screen.height / 2 -buttonHeight/2-buttonHeight-buttonHeight/4;resetButtonX=Screen.width / 2 ;resetButtonY=Screen.height / 2 +buttonWidth+buttonWidth/2;resultMsgX=Screen.width / 2 -buttonWidth/2-buttonWidth;resultMsgY=Screen.height / 2 -2*buttonHeight-buttonHeight/4;//结果提示字体风格resultMsgStyle = new GUIStyle {fontSize = 20,fontStyle = FontStyle.Bold};//按键字体风格ButtonStyle = new GUIStyle("button");ButtonStyle.fontSize = 20;resultMsgStyle.normal.textColor = Color.black;//背景设置GUIStyle backgroundStyle = new GUIStyle();backgroundStyle.normal.background = img;GUI.Label(new Rect(0, 0, Screen.width, Screen.height), "", backgroundStyle);//放置按钮//回退按钮displayBackButton();//重新开始按钮displayResetButton();//展示结果提示(展示检查是否有胜利者的结果)ShowResult(Check());//展示棋盘实现下棋逻辑showCheckerboard(firstGridX,firstGridY);GUI.enabled = true;

其中displayBackButton()实现如下:

    private void displayBackButton(){if (GUI.Button(new Rect(backButtonX, backButtonY,buttonWidth/2+buttonWidth, buttonHeight / 2), "返回菜单",ButtonStyle)) {SceneManager.LoadScene("StartMenu", LoadSceneMode.Single);//切换到"StartMenu"界面并销毁开始菜单界面}}

displayResetButton()实现如下:

    private void displayResetButton(){if (GUI.Button(new Rect(resetButtonX, resetButtonY,buttonWidth/2+buttonWidth, buttonHeight / 2), "重新开始",ButtonStyle)) {Reset();return;}}

检查函数Check(),检查是否有胜利者产生,函数首先检查斜行,为了实现代码的简洁,使用相对坐标,通过(i+1)%3,(i+2)%3找到当前横或纵坐标的另外两个横或纵坐标,实现如下:

    /*检查是否有赢家出现*/private int Check() {for(int i=0;i<3;i++){//占有斜行if(checkerboard[i,i]!=empty){//如果斜右下方向被同一角色占有if(checkerboard[(i+1)%3,(i+1)%3]==checkerboard[i,i]&&checkerboard[(i+2)%3,(i+2)%3]==checkerboard[i,i]) return checkerboard[i,i];}if(checkerboard[i,2-i]!=empty){//如果斜右上方向被同一角色占有if(checkerboard[(i+1)%3,2-(i+1)%3]==checkerboard[i,2-i]&&checkerboard[(i+2)%3,2-(i+2)%3]==checkerboard[i,2-i]) return checkerboard[i,2-i];}//占有横纵行for(int j=0;j<3;j++){if(checkerboard[i,j]!=empty){//占有横行if(checkerboard[i,(j+1)%3]==checkerboard[i,j]&&checkerboard[i,(j+2)%3]==checkerboard[i,j]) return checkerboard[i,j];//占有纵行if(checkerboard[(i+1)%3,j]==checkerboard[i,j]&&checkerboard[(i+2)%3,j]==checkerboard[i,j]) return checkerboard[i,j];}}}return empty;
}

Check()函数返回胜利的对象,如果没有返回empty;

ShowResult函数接收Check()函数的返回结果并对应打印"你赢了",“你输了”,或"目前没有赢家" 的结果,如下:

    /*展现当前结果(输赢)*/private void ShowResult(int winner){//Check if someone winsif (winner != empty) {if(winner == you)GUI.Label(new Rect(resultMsgX, resultMsgY, 100, 100), "你赢了" , resultMsgStyle);elseGUI.Label(new Rect(resultMsgX, resultMsgY, 100, 100), "你输了" , resultMsgStyle);playing = !playing;GUI.enabled = false;}else{GUI.Label(new Rect(resultMsgX, resultMsgY, 100, 100), "目前没有赢家" , resultMsgStyle);}}

棋盘的展示和功能的实现函数showCheckerboard如下, GUI.Button按照设计要求在每一次刷新的时候显示:

  1. 棋格逻辑值为you时,显示“X”
  2. 棋格逻辑值为pc时,显示“O"
  3. 棋格逻辑值为empty时,显示“",点击后可以相应更改其逻辑值(改为turn的值,即设置为当前玩家点击后占有),在下一轮刷新时显示出来,PC通过调用searchForGoodStep()函数,选择最优的空白棋格进行填写。
    //展示棋盘按钮并实现下棋的功能private void showCheckerboard(int firstGridX,int firstGridY){for (int i = 0; i < 3; ++i) {for (int j = 0; j < 3; ++j) {if (checkerboard[i, j] == you) {//you下的棋用“X”表示,PC端下的棋用“O"表示GUI.Button(new Rect(firstGridX + i * buttonWidth, firstGridY + j * buttonHeight, buttonWidth, buttonHeight), "X");} else if (checkerboard[i, j] == pc) {GUI.Button(new Rect(firstGridX + i * buttonWidth, firstGridY + j * buttonHeight, buttonWidth, buttonHeight), "O");} else {if (playing) {//当轮到玩家下棋的时候,玩家点击空的棋格,该棋格被玩家占有并显示"X"if (turn==you) {if (GUI.Button(new Rect(firstGridX + i * buttonWidth, firstGridY + j * buttonHeight, buttonWidth, buttonHeight), "")) {checkerboard[i, j] = you;turn = pc;}} else {//当轮到电脑下棋的时候,电脑按照最优测量选择空的棋格,该棋格被电脑占有并显示"O"GUI.Button(new Rect(firstGridX + i * buttonWidth, firstGridY + j * buttonHeight, buttonWidth, buttonHeight), "");searchForGoodStep();turn = you;}}}}}}

按照pc端最优选择算法的设计思路,searchForGoodStep()函数应该实现如下,其中makePCWin()搜索一个空位使得PC能赢,该函数找到符合要求的位置时,返回true,否则继续调用blockYou搜索使you赢的空位,占有它,从而阻塞you,该函数找到符合要求的位置时,返回true,否则继续调用randomStep存储所有的空格并随机选择一个作为下棋空位,如果以上三个函数都找不到合适的位置,表示棋格已经占满,直接返回,否则填写选择的棋格逻辑值为pc。

    private void searchForGoodStep(){if(makePCWin()){}else if (blockYou()) {}else if(randomStep()){}else return;checkerboard[PC_x, PC_y] = pc;}

和check函数的实现类似,makePCWin一次查找斜行,横纵行,寻找一个行,满足拥有一个空位,和两个逻辑值为pc的棋格,如下:

    /*搜索一个空位使得PC能赢*/private bool makePCWin() {for(int i=0;i<3;i++){//占有斜行if(checkerboard[i,i]==empty){//如果斜右下方向其他空格被pc占有if(checkerboard[(i+1)%3,(i+1)%3]==pc&&checkerboard[(i+2)%3,(i+2)%3]==pc){PC_x=i;PC_y=i;return true;}}if(checkerboard[i,2-i]==empty){//如果斜右上方向其他空格被pc占有if(checkerboard[(i+1)%3,2-(i+1)%3]==pc&&checkerboard[(i+2)%3,2-(i+2)%3]==pc){PC_x=i;PC_y=2-i;return true;}}//占有横纵行for(int j=0;j<3;j++){if(checkerboard[i,j]==empty){//占有横行if(checkerboard[i,(j+1)%3]==pc&&checkerboard[i,(j+2)%3]==pc){PC_x=i;PC_y=j;return true;}//占有纵行if(checkerboard[(i+1)%3,j]==pc&&checkerboard[(i+2)%3,j]==pc){PC_x=i;PC_y=j;return true;}}}}return false;}

blockYou() 同理,寻找一个行,满足拥有一个空位,和两个逻辑值为you的棋格。

randomStep()函数则使用List emptySpace = new List();语句定义的

emptySpace 数组存放所有空格的坐标。通过System.Random获得随机角标,从而或得随机坐标。

Reset()实现棋格重置为空并重新设置turn为有,游戏状态playing为进行中。

6.3 双人游戏界面:

界面组件的存放和单人游戏相同,不同的是玩家从“玩家和电脑”变成了”玩家一和玩家二“,在逻辑上只需要将PC的行走策略修改成玩家二的行走策略,即//当轮到玩家2下棋的时候,玩家2点击空的棋格,该棋格被玩家2占有并显示"O",修改showCheckerboard()函数如下:

    //展示棋盘按钮并实现下棋的功能private void showCheckerboard(){for (int i = 0; i < 3; ++i) {for (int j = 0; j < 3; ++j) {if (checkerboard[i, j] == player1) {//player1下的棋用“X”表示,player2下的棋用“O"表示GUI.Button(new Rect(firstGridX + i * buttonWidth, firstGridY + j * buttonHeight, buttonWidth, buttonHeight), "X");} else if (checkerboard[i, j] == player2) {GUI.Button(new Rect(firstGridX + i * buttonWidth, firstGridY + j * buttonHeight, buttonWidth, buttonHeight), "O");} else {if (playing&&GUI.Button(new Rect(firstGridX + i * buttonWidth, firstGridY + j * buttonHeight, buttonWidth, buttonHeight), "")) {if (turn==player1) {//当轮到玩家1下棋的时候,玩家1点击空的棋格,该棋格被玩家1占有并显示"X"checkerboard[i, j]=player1;turn = player2;} else {//当轮到玩家2下棋的时候,玩家2点击空的棋格,该棋格被玩家2占有并显示"O"checkerboard[i, j]=player2;turn = player1;}}}}}}

基于Unity开发的井字棋游戏设计相关推荐

  1. C++实现的基于α-β剪枝算法的井字棋游戏

    "井字棋"游戏(又叫"三子棋"),是一款十分经典的益智小游戏,操作简单,娱乐性强.两个玩家,一个打圈(O),一个打叉(X),轮流在3乘3的格上打自己的符号,最先 ...

  2. [文档和源码分享]C++实现的基于α-β剪枝算法的井字棋游戏

    "井字棋"游戏(又叫"三子棋"),是一款十分经典的益智小游戏,操作简单,娱乐性强.两个玩家,一个打圈(O),一个打叉(X),轮流在3乘3的格上打自己的符号,最先 ...

  3. Python基础编程案例:简单的井字棋游戏设计与制作

    本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 前言 python井字棋游戏虽然看上去非常简陋,但是却非常值得学习. 先看怎么玩 ...

  4. python井字棋游戏开发(人人对战,人机对战,包含源码,逻辑思维流程图)

    需求分析 井字棋是比较便捷休闲娱乐的一种迷你棋,玩法比较简单,只需要一个九宫格棋盘就可以实现两人对战,规则为谁先连成三个棋子的一条线即可获胜.本游戏,需要满足两个主要功能:1.能实现玩家对战:2.能实 ...

  5. Unity3D学习笔记(2)——用GUI制作井字棋游戏

    本来拿gui来做游戏蛮怪的,但这却是一个熟悉gui的不错的途径.今天我就学着使用GUI.Button做了一个十分简单的井字棋游戏.上个成品图: 首先创建一个C#脚本文件,去掉Update方法,因为这里 ...

  6. 采用α-β算法实现井字棋游戏

    题目描述 (1)图形化界面. (2)随机选取先手后手. (3)可以人-计算机或计算机-计算机 界面效果 算法 基本思想 Max-Min算法: 采用Max-Min算法进行对抗搜索,Max和Min双方均要 ...

  7. java——博弈算法实现井字棋游戏

    通过java语言开发了一个简单的井字棋游戏.主要有6个类,其中有一个是主类(Main.java),一个是抽象类(PiecesMove.java)组成. 下面对各个类简单介绍一下: TicTicToe. ...

  8. php井字游戏,python实现井字棋游戏

    #本游戏python3.4.0下编写调试,只能在windows下运行. import random import subprocess import time #定义函数 def draw_board ...

  9. Minimax 和 Alpha-beta 剪枝算法简介,及以此实现的井字棋游戏(Tic-tac-toe)

    前段时间用 React 写了个2048 游戏来练练手,准备用来回顾下 React 相关的各种技术,以及试验一下新技术.在写这个2048的过程中,我考虑是否可以在其中加入一个 AI 算法来自动进行游戏, ...

  10. C++井字棋游戏,DOS界面版

    据说有一个能保证不败的算法.明天看看先再写个PVC版的. 正题.今天无聊写了个井字棋游戏,顺便逐渐让自己习惯良好的代码风格,放上来给新手学习学习. jzq2.cpp /*N字棋游戏PVP版,DOS版本 ...

最新文章

  1. 【JOURNAL】《不思八九》 --和友腊八诗一首
  2. Android模拟器安装程序及上传音乐并播放
  3. Java OAuth开发包资料
  4. 典型案例道出“服务台”的价值
  5. 基于webpack的PC端和mobile端开发以及生产环境的搭建
  6. 一路踩坑,被迫聊聊 C# 代码调试技巧和远程调试
  7. php sslbug,PHP错误抑制符(@)导致引用传参失败Bug的分析
  8. 第一章 对象引论02
  9. 【Elastischearch】Elastischearch bulk 请求源码
  10. 使用AIDL实现进程间的通信
  11. linux开启hadoop服务,Hadoop 2.7.4 关闭与启动
  12. [纪事]再见,CodeArtist
  13. 从Java类库看设计模式(4)
  14. 简单实用的Android ORM框架TigerDB
  15. Spring--超简单利用quartz实现定时作业
  16. EmEditor中,正则判断行中是否存在自动字符串
  17. 聚类-----高斯混合模型
  18. 【SENCHA TOUCH】改了tomcat的IP访问!java的session失效问题! [ Web 开发]
  19. layui怎么设置select默认选中,修改回显
  20. kubeadm部署1.11.1的k8s集群

热门文章

  1. 微信小程序直播功能服务条款 禁止哪些商品
  2. 等额本金和等额本息还款方式的差异分析
  3. python 输入一个整数,将该整数逆向输出
  4. 《Excel 小技巧》之 一个单元格换行显示日期和星期
  5. windows无法完成安装 若要在此计算机上安装_Win10无法启动,主引导记录(MBR)损坏,用这个方法快速修复...
  6. 计算机的认识文档,对计算机专业的认识.pdf
  7. scala_day01_安装_基础_IO_函数_递归_异常_方法_样例类_伴生对象
  8. 税务会计实务【18】
  9. 深交所“区块链50指数”,成分股的成色几何?
  10. TA技术美术学习路线