欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

井字棋的实现

程序员文章站 2024-03-18 23:56:16
...

unity实现井字棋

前言

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

要求

仅允许使用 IMGUI 构建 UI

目的

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

完成效果图

井字棋的实现

完成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)

相关标签: c# unity3d