Unity3d学习之路-简单井字棋

Unity3d-简单井字棋


  • 作业目的:熟悉IMGUI的使用,和基础的Unity3d操作
  • 游戏玩法:选择两个模式,1.Player vs Player 2.Computer vs Player,当其中一种棋子连成三个则这个棋子的玩家获胜。
  • 技术限制:仅允许使用IMGUI构成UI

游戏实现

  • 首先搭建游戏菜单界面:

    • 通过GUIStyle设置字体大小和颜色
    • 使用GUI.Label创建文本,GUI.Label的第一个参数是Rect类型的位置,表示标签在屏幕上的矩形位置,Rect中的参数分别是:起点x坐标,起点y坐标,标签宽度,标签高度。第二个参数text类型是String,标签的内容,第三个参数style类型是GUIStyle,标签使用的样式。
      Unity3d学习之路-简单井字棋_第1张图片

    • 使用GUI.Button来创建按钮,参数列表与GUI.Label类似,用if来判断Button是否被点击,若点击了则进入相应的游戏模式


GUIStyle fontStyle = new GUIStyle()
{
    fontSize = 25
};
fontStyle.normal.textColor = new Color(255, 255, 255);
GUIStyle fontStyle1 = new GUIStyle()
{
    fontSize = 30
};
fontStyle1.normal.textColor = new Color(255, 255, 255);
GUI.Label(new Rect(413, 50, 100, 50), "井字游戏", fontStyle1);

if(gamestate == GameState.end)
{
    if (GUI.Button(new Rect(400, 200, 140, 50), "Player vs Player"))
    {
        gamestate = GameState.mode1;
        isWin = false;
    }
    if (GUI.Button(new Rect(400, 280, 140, 50), "Player vs Computer"))
    {
        gamestate = GameState.mode2;
        isWin = false;
    }
}
  • 不同游戏模式的游戏逻辑
    • Player vs Player
      • 使用循环建立3X3的棋盘,因为这段代码在OnGUI中,每一帧监控空白格子是否被按下,从而实现落子
      • 陷阱:判断棋盘每一格的值从而建立Button的内容是X还是O,应该写在判断空白格子被点击前面,否则会造成看似已经落过子但是可以重新点击。(也可以在空白格子被点击的判断条件中加入判断棋盘board的值)
   if(gamestate == GameState.mode1)
{
    FixedUI(fontStyle);
    bool full = true;
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            if (board[i, j] == 1)
            {
                GUI.Button(new Rect(350 + i * 80, 100 + j * 80, 80, 80), "X");
            }
            else if(board[i, j] == 2)
            {
                GUI.Button(new Rect(350 + i * 80, 100 + j * 80, 80, 80), "O");
            }
            else
            {
                full = false;
            }
             //如果空白格子被点击
            if (GUI.Button(new Rect(350 + i * 80, 100 + j * 80, 80, 80), "")) 
            {
                if(!isWin)
                {
                    if (click == 1)        //X的回合   
                    {
                        board[i, j] = 1;   //棋盘下X                      
                    }
                    if (click == -1)       //O的回合                   
                    {
                        board[i, j] = 2;  //棋盘下O            
                    }
                    click = -click;
                }
            }
        }
    }
  • Compuert vs Player
    • 检测AI是否有获胜的机会以及玩家是否有获胜的机会来判断落子,玩家回合逻辑与Player vs Player逻辑相同
      • 我的AI逻辑是是否AI有两颗子连在一起,如果有则下一步下子让他们连成三个,若没有,则判断玩家是否有两颗子连在一起,如果有则下子堵住这两个,若没有,则随机落子。(因为前面两个逻辑是一样的,所以封装为同一函数,用mod来标记检测的是AI还是玩家)
bool CheckGo(int pos_x,int pos_y,int mod)       //检查AI和玩家是否有获胜机会
{
    int piecesNum = 0;
    for (int i = 0; i < 3; i++)
    {
        piecesNum = 0;
        bool empty = false;
        for (int j = 0; j < 3; j++)
        {
            if (board[i, j] == mod)
            {
                piecesNum++;
            }
            else if (board[i, j] == 0)
            {
                pos_x = i;
                pos_y = j;
                empty = true;
            }
        }
        if (piecesNum == 2 && empty && board[pos_x,pos_y] == 0)
        {
            board[pos_x, pos_y] = 1;
            click = -click;
            return true;
        }
    }
    for (int i = 0; i < 3; i++)
    {
        piecesNum = 0;
        bool empty = false;
        for (int j = 0; j < 3; j++)
        {
            if (board[j, i] == mod)
            {
                piecesNum++;
            }
            else if (board[j, i] == 0)
            {
                pos_x = j;
                pos_y = i;
                empty = true;
            }
        }
        if (piecesNum == 2 && empty && board[pos_x, pos_y] == 0)
        {
            board[pos_x, pos_y] = 1;
            click = -click;
            return true;
        }
    }
    piecesNum = 0;
    bool empty1 = false;
    for (int i = 0; i < 3; i++)
    {     
        if (board[i, i] == mod)
        {
            piecesNum++;
        }
        else if (board[i, i] == 0)
        {
            pos_x = i;
            pos_y = i;
            empty1 = true;
        }
    }
    if (piecesNum == 2 &&empty1 && board[pos_x, pos_y] == 0)
    {
        board[pos_x, pos_y] = 1;
        click = -click;
        return true;
    }
    piecesNum = 0;
    empty1 = false;
    for (int i = 0; i < 3; i++)
    {
        int j = 2 - i;
        if (board[i, j] == mod)
        {
            piecesNum++;
        }
        else if (board[i, j] == 0)
        {
            pos_x = i;
            pos_y = j;
            empty1 = true;
        }
    }
    if (piecesNum == 2 && empty1 && board[pos_x, pos_y] == 0)
    {
        board[pos_x, pos_y] = 1;
        click = -click;
        return true;
    }
    return false;
}
  • 在OnGUI中轮到AI的回合的代码
    • 这里在判断是否AI和玩家取胜之后,如果都不满足则随机落子
    • 陷阱:在这里遇到玩家落子的时候,Unity就卡住了,只能用任务管理器直接结束掉进程,而且不知道什么时候会出现这种情况,卡住后无法获得任何引起bug的信息。后来多次查找后发现是while循环的问题,因为随机查找空的格子,可能导致很长时间无法找到,所以增加了计数器,如果5次随机位置都是下过子的地方,则手动找到一个空的位置,让它落子。
if (click == 1 && !isWin)        //AI的回合
{
    int a = 1, b = 2, c = 0;    //1代表检测X是否有取胜机会,2代表检测O是否有取胜机会
    if (!CheckGo(c, c, a))
    {
        if (!CheckGo(c, c, b))
        {
            int pos_x, pos_y;
            System.Random ran = new System.Random();
            pos_x = ran.Next(0, 2);
            pos_y = ran.Next(0, 2);
            int count = 0;
            while (board[pos_x, pos_y] != 0 && Check() == 0)
            {
                pos_x = ran.Next(0, 2);
                pos_y = ran.Next(0, 2);
                count++;
                if(count == 5)
                {
                    FindEmpty();
                    break;
                }
            }
            if(count != 5)
            {
                board[pos_x, pos_y] = 1;
                click = -click;
            }
        }
    }
}
  • 判断游戏结束和其他按键的搭建
void FixedUI(GUIStyle fontStyle)            //固定不变的UI
{
    int result = Check();
    if (result == 1)
    {
        GUI.Label(new Rect(430, 350, 100, 50), "X wins!", fontStyle);
        isWin = true;
    }
    else if (result == 2)
    {
        GUI.Label(new Rect(430, 350, 100, 50), "O wins!", fontStyle);
        isWin = true;
    }

    if (GUI.Button(new Rect(420, 390, 100, 50), "Reset"))
    {
        Reset();
        isWin = false;
    }
    if (GUI.Button(new Rect(420, 450, 100, 50), "Return"))
    {
        Reset();
        gamestate = GameState.end;
        isWin = false;
    }
    if (!isWin && click == 1)
    {
        GUI.Label(new Rect(430, 350, 100, 50), "X turn", fontStyle);
    }
    if (!isWin && click == -1)
    {
        GUI.Label(new Rect(430, 350, 100, 50), "O turn", fontStyle);
    }
}
    void Reset()
    {
        for (int i = 0; i < 3; i++)
            for (int j = 0; j < 3; j++)
                board[i, j] = 0;                   //每一个小格子都没有棋子
    }

最后实现结果如图

  • 背景由Plane组成,将相机调整到拍摄整个Plane的位置,将c#代码挂载到Plane上
    Unity3d学习之路-简单井字棋_第2张图片
    Unity3d学习之路-简单井字棋_第3张图片

完整代码见github地址:井字棋

你可能感兴趣的:(unity3d)