如何使用Unity3D开发一款拥有人工智能的五子棋游戏

1,279 阅读2分钟

我正在参加掘金社区游戏创意投稿大赛团队赛,详情请看:游戏创意投稿大赛

前言

最近看到稀土掘金有一个游戏开发的活动,奖品很丰富,于是我就和我的朋友(掘金主页:juejin.cn/user/195232… 一起参加了这个活动,一起开发了一款具有人工智能AI 的五子棋游戏,游戏是基于 Unity3D 引擎打造的,游戏中的 AI 正式目前谷歌 DeepMind 团队打造的 AlphaGo。

202242094149.bmp

环境搭建

Unity3D 引擎版本:2018.3.8f1,下载地址:unity3d.com/cn/get-unit…

AlphaGo 插件下载:s3.amazonaws.com/unity-ml-ag…

导入资源

  1. 将下载好的插件放入 Assets 文件夹内,双击导入 AlphaGo
  2. 创建 UI,将 Action Policy Text.prefab Game Canvas.prefab Hint Canvas.prefab 导入 Prefabs 文件中
  3. 将 Gobang 文件夹放入 Resources 文件夹中
  4. 将场景文件 Gobang10x10.unity 拖入 Scenes 文件夹
  5. 替换 Sprites 文件夹中的内容

功能开发

核心代码如下:

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class GobangGame : Game
{
    public Piece[] pieces; // 所有棋子
    [HideInInspector] public PieceType[] boardSituation;
    
    private PieceType thisTurn;
    private PieceType playerPieceType { get { return playerFirst ? PieceType.Black : PieceType.White; } }

    private int width;
    private GobangPolicyValueNet policyValueNet;
    private MCTSPlayer mctsPlayer;
    private GobangBoard board;

    public override string PlayerFirstWin { get { return width + "PlayerFirstWin"; } }
    public override string PlayerFirstLose { get { return width + "PlayerFirstLose"; } }
    public override string PlayerFirstTie { get { return width + "PlayerFirstTie"; } }
    public override string AiFirstWin { get { return width + "AIFirstWin"; } }
    public override string AiFirstLose { get { return width + "AIFirstLose"; } }
    public override string AiFirstTie { get { return width + "AIFirstTie"; } }
    public override string VersionKey { get { return width + "GobangVersion"; } }

    protected override void Start()
    {
        width = (int)Mathf.Sqrt(pieces.Length);
        base.Start();

        boardSituation = new PieceType[pieces.Length];
        for (int i = 0; i < pieces.Length; i++)
        {
            pieces[i].index = i;
        }
        
        policyValueNet = new GobangPolicyValueNet(width);
        mctsPlayer = new MCTSPlayer(policyValueNet);
        board = new GobangBoard(width, 5);
    }

    public override void StartGame()
    {
        for (int i = 0; i < pieces.Length; i++)
        {
            pieces[i].SetPiece(PieceType.Empty);
            boardSituation[i] = PieceType.Empty;
        }
        thisTurn = PieceType.Black;

        board.InitBoard();
        mctsPlayer.SetPlayerInd(ReversiBoard.players[playerFirst ? 1 : 0]);
        mctsPlayer.ResetPlayer();

        StartCoroutine("StartGameLater");
    }
    private IEnumerator StartGameLater()
    {
        yield return null;
        gameRunning = true;
    }

    // 玩家或电脑输入
    private void Update()
    {
        if (!gameRunning) return;

        if (thisTurn == playerPieceType) // 轮到玩家
        {
            Vector3 pointedPosition;
            if (Input.GetMouseButtonDown(0)) // 鼠标操作
            {
                pointedPosition = Input.mousePosition;
            }
            else if (Input.touchCount == 1) // 触屏操作
            {
                pointedPosition = Input.touches[0].position;
            }
            else return;

            RaycastHit2D hit2D = Physics2D.CircleCast(Camera.main.ScreenToWorldPoint(pointedPosition), 0.1f, Vector2.zero);
            if (hit2D.collider == null) return; // 没点到下棋子的位置
            Piece hitPiece = hit2D.collider.GetComponent<Piece>();
            if (boardSituation[hitPiece.index] != PieceType.Empty) return; // 点的位置已经有棋子了

            // 成功下棋
            hitPiece.SetPiece(thisTurn);
            boardSituation[hitPiece.index] = thisTurn;
            EndThisTurn(hitPiece.index);
            hintCanvas.HideHint();
        }
        else // 轮到电脑
        {
            int move = mctsPlayer.GetAction(board);
            pieces[move].SetPiece(thisTurn);
            boardSituation[move] = thisTurn;
            EndThisTurn(move);
        }
    }

    private void EndThisTurn(int move)
    {
        // 判断是否有胜负
        board.DoMove(move);
        object[] endResult = board.GameEnd();
        bool end = (bool)endResult[0];
        int winner = (int)endResult[1];
        if (end)
        {
            EndGame(winner);
            return;
        }

        // 进入下一步
        thisTurn = (thisTurn == PieceType.Black ? PieceType.White : PieceType.Black);
    }

    private void EndGame(int winner)
    {
        string endText;
        if (winner == mctsPlayer.player)
        {
            endText = "你输了";
            if (playerFirst) PlayerPrefs.SetInt(PlayerFirstLose, PlayerPrefs.GetInt(PlayerFirstLose, 0) + 1);
            else PlayerPrefs.SetInt(AiFirstLose, PlayerPrefs.GetInt(AiFirstLose, 0) + 1);
        }
        else if (winner == -1)
        {
            endText = "平局";
            if (playerFirst) PlayerPrefs.SetInt(PlayerFirstTie, PlayerPrefs.GetInt(PlayerFirstTie, 0) + 1);
            else PlayerPrefs.SetInt(AiFirstTie, PlayerPrefs.GetInt(AiFirstTie, 0) + 1);
        }
        else
        {
            endText = "你赢了";
            if (playerFirst) PlayerPrefs.SetInt(PlayerFirstWin, PlayerPrefs.GetInt(PlayerFirstWin, 0) + 1);
            else PlayerPrefs.SetInt(AiFirstWin, PlayerPrefs.GetInt(AiFirstWin, 0) + 1);
        }

        gameRunning = false;
        gameEndText.text = endText;
        gameEndText.gameObject.SetActive(true);
        Invoke("EndGame", 2f);
    }

    public override void Hint()
    {
        if (hintCanvas.showing)
            hintCanvas.HideHint();
        else
            hintCanvas.ShowHint(thisTurn == playerPieceType ? policyValueNet.PolicyValueFn(board) : null, actionTransforms);
    }
}

代码比较多,这里就不做多阐述,如果要看完整的工程代码,可在文末找到链接下载游戏。

打包运行

在 Unity3D 的编辑器中选择 File->BuildSetting,点击 Build and Run,即可对游戏进行打包运行。

202242092912.bmp

202242095517.bmp

最后

本文到这里就结束了,因为整理的时间有限,所以我在此文中没有对游戏功能开发做详细的解释说明,如果你对我的游戏很感兴趣,可以通过下方的链接来获取我的整个工程代码。技术路漫漫,唯有精益求精方能彰显智慧,谢谢你的阅读。

github.com/ShenJieSuzh…