井字棋的实现

unity实现井字棋

前言

这是中山大学2020年3D游戏设计第二次作业代码题部分,如有错误,欢迎指正,感谢您的阅读!

要求

仅允许使用 IMGUI 构建 UI

目的

  • 了解 OnGUI() 事件,提升 debug 能力
  • 提升阅读 API 文档能力

完成效果图

效果图

完成gif演示

gif演示

功能

本代码除井字棋基本的游戏之外,还实现了简单的ai和悔棋的功能。

具体实现

变量定义

这部分实现的是所要使用的变量

    private int turn = 1; //1 - Player 1, 2 - Player 2 
    private int[,] board = new int[3, 3];   //设置3*3棋盘
    private int result = 0;//0代表未结束,1为1赢,2为2赢,3为平局
    private int[,] retract = new int[3, 3];   //设置下棋记录
    private int step = 0;//步数,用于恢复
    //背景及棋子
    public Texture2D backgrounds;
    public Texture2D arrows;
    public Texture2D player1;
    public Texture2D player2;

开始

开始即调用初始化函数进行初始化即可

    void Start()
    {
        Reset();
    }

重置

这部分用于初始化和重置

    void Reset()
    {
        turn = 1;
        result = 0;
        for (int i = 0; i < 3; i++)
            for (int j = 0; j < 3; j++)
            {
                board[i, j] = 0;
                retract[i, j] = 0;
            }
        step = 0;
    }

检测是否胜利

用一个函数判断是否胜利,胜利需要分为横纵,交查或已经被填满三种情况考虑,并返回状态码。

    int check()
    {
        int total = 0;
        for (int i = 0; i < 3; i++)
            for (int j = 0; j < 3; j++)
                if (board[i, j] != 0) total++;
        for(int i = 0; i < 3; i++)
        {
            if (board[i, 0] != 0 && board[i, 0] == board[i, 1] && board[i, 0] == board[i, 2]) return board[i, 0];
            if(board[0, i] != 0 && board[0, i] == board[1, i] && board[0, i] == board[2, i]) return board[0, i];
        }//判断横纵
        if (board[1, 1] != 0 && (
            (board[0, 0] == board[1, 1] && board[1, 1] == board[2, 2]) ||
            (board[0, 2] == board[1, 1] && board[2, 0] == board[1, 1]))) return board[1, 1];//判断交叉
        if (total == 9) return 3;//判断是否满
        return 0;//未结束
    }

提示部分

本部分会自动帮你走一步,优先级为:自己胜利,对手马上胜利,对手制造双2,按序下,代码很简单,不再赘述。

   void helpme()//一个人工智障
    {
        for (int i = 0; i < 3; i++)
            for (int j = 0; j < 3; j++)
            {
                if (board[i, j] != 0) continue;
                board[i, j] = turn;
                if (check()!=0) return;
                else board[i, j] = 0;
            }//判断是否可以赢
        for (int i = 0; i < 3; i++)
        {
            if (board[i, 0] != 0 && board[i, 0] == board[i, 1] && board[i, 2] == 0) { board[i, 2] = turn; setretract(i, 2); return; }
            if (board[i, 1] != 0 && board[i, 1] == board[i, 2] && board[i, 0] == 0) { board[i, 0] = turn; setretract(i, 0); return; }
            if (board[i, 2] != 0 && board[i, 0] == board[i, 2] && board[i, 1] == 0) { board[i, 1] = turn; setretract(i, 1); return; }
            if (board[0, i] != 0 && board[0, i] == board[1, i] && board[2, i] == 0) { board[2, i] = turn; setretract(2, i); return; }
            if (board[1, i] != 0 && board[1, i] == board[2, i] && board[0, i] == 0) { board[0, i] = turn; setretract(0, i); return; }
            if (board[2, i] != 0 && board[0, i] == board[2, i] && board[1, i] == 0) { board[1, i] = turn; setretract(1, i); return; }
        }//判断横纵
        if (board[1, 1] != 0)
        {
            if (board[0, 0] == board[1, 1] && board[2, 2] == 0) { board[2, 2] = turn; setretract(2, 2); return; }
            if (board[2, 2] == board[1, 1] && board[0, 0] == 0) { board[0, 0] = turn; setretract(0, 0); return; }
            if (board[0, 2] == board[1, 1] && board[2, 0] == 0) { board[2, 0] = turn; setretract(2, 0); return; }
            if (board[2, 0] == board[1, 1] && board[0, 2] == 0) { board[0, 2] = turn; setretract(0, 2); return; }
        }
        else
        {
            if (board[0, 0] == board[2, 2] || board[0, 2] == board[2, 0]) { board[1, 1] = turn;setretract(1, 1);return; }
        }//判断对角
        int cmp = turn % 2 + 1;
        for (int i = 0; i < 3; i++)
            for (int j = 0; j < 3; j++)
            {
                if (board[i, j] == cmp && board[j, i] == 0)
                {
                    board[j, i] = turn;
                    setretract(j, i);
                    return;
                }
            }//防止对手下双二
        for (int i = 0; i < 3; i++)
            for (int j = 0; j < 3; j++)
            {
                if (board[i, j] == 0)
                {
                    board[i, j] = turn;
                    setretract(i, j);
                    return;
                }
            }//按序放置
    }

悔棋

我们使用另外一个3*3的数组来存储下棋的顺序,并使用一个变量来存储当前的步数,设置和悔棋函数如下:

    void retractone()
    {
        if (step == 0) return;
        for (int i = 0; i < 3; i++)
            for (int j = 0; j < 3; j++)
                if (retract[i, j] == step)
                {
                    step--;
                    retract[i, j] = 0;
                    board[i, j] = 0;
                    turn = turn % 2 + 1;
                    return;
                }
    }
    void setretract(int i,int j)
    {
        step++;
        retract[i, j] = step;
    }

UI界面

最后,是UI界面的实现。UI界面的难点在于调整位置,需要计算好每个框的大小和位置,并设置好按钮。

    void OnGUI()
    {
        GUIStyle playingstyle = new GUIStyle();
        playingstyle.normal.textColor = new Color(0, 0, 1);
        playingstyle.fontSize = 40;//playing字体
        GUIStyle winstyle = new GUIStyle();
        winstyle.normal.textColor = new Color(0, 1, 0);
        winstyle.fontSize = 40;//win字体
        GUIStyle drawstyle = new GUIStyle();
        drawstyle.normal.textColor = new Color(1, 0, 0);
        drawstyle.fontSize = 40;//draw字体
        GUIStyle titlestyle = new GUIStyle();
        titlestyle.normal.textColor = new Color(1, 1, 1);
        titlestyle.fontSize = 40;//playing字体
        GUIStyle playerfont = new GUIStyle();
        playerfont.normal.textColor = new Color(1, 0, 0);
        playerfont.fontSize = 23;//player字体
        //设置背景及基本UI
        GUI.Label(new Rect(0, 0, 640, 320), backgrounds);
        GUI.Label(new Rect(40, 10, 90, 40), "Tic Tac Toe", titlestyle);
        GUI.Label(new Rect(65, 50, 50, 50), player1);
        GUI.Label(new Rect(130, 65, 90, 40), "Player1", playerfont);
        GUI.Label(new Rect(65, 120, 50, 50), player2);
        GUI.Label(new Rect(130, 135, 100, 50), "Player2", playerfont);
        if (turn == 1) GUI.Label(new Rect(20, 50, 100, 50), arrows);
        else GUI.Label(new Rect(20, 120, 100, 50), arrows);
        if (GUI.Button(new Rect(60, 200, 70, 50), "Restart")) Reset();
        //设置井字棋棋盘
        for (int i = 0; i < 3; ++i)
            for (int j = 0; j < 3; ++j)
            {
                if (board[i, j] == 1)
                    GUI.Button(new Rect(320 + i * 70, 80 + j * 70, 70, 70), player1);
                if (board[i, j] == 2)
                    GUI.Button(new Rect(320 + i * 70, 80 + j * 70, 70, 70), player2);
                if (GUI.Button(new Rect(320 + i * 70, 80 + j * 70, 70, 70), "")) 
                {
                    if (result == 0)
                    {
                        board[i, j] = turn;
                        turn = turn % 2 + 1;
                        setretract(i, j);
                    }
                }
            }
        //结果输出
        result = check();
        if (result == 1)
        {
            GUI.Label(new Rect(320, 20, 100, 50), "Player1 wins!", winstyle);
        }
        else if (result == 2)
        {
            GUI.Label(new Rect(320, 20, 100, 50), "Player2 wins!", winstyle);
        }
        else if (result == 3)
        {
            GUI.Label(new Rect(320, 20, 100, 50), "In a draw!", drawstyle);
        }
        else
        {
            GUI.Label(new Rect(320, 20, 100, 50), "Playing...", playingstyle);
        }
        //调用自动走一步
        if (GUI.Button(new Rect(160, 200, 70, 50), "Help Me") && result == 0)
        {
            helpme();
            turn = turn % 2 + 1;
        }
        //调用悔棋
        if (GUI.Button(new Rect(110, 260, 70, 50), "Retract") && result == 0)
        {
            retractone();
        }

    }

将上面所有代码组合到一个类中,即可生成脚本。

项目完整文件

如果有需要图片素材资源可以自取(图片都是网上图片ps抠出来的),位于asset/scene中。

项目完整文件地址(gitee)

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值