【C#】【Unity 五子棋 2D 游戏技术实现】

一、系统概述

该五子棋游戏基于 Unity 引擎开发,实现了 15x15 标准棋盘的 2D 对战功能,包含棋盘渲染、落子交互、胜负判定、悔棋和重新开始等核心功能。系统由两个主要脚本组成:

  • Board2DSetup:负责棋盘界面的初始化,包括背景图像和网格线的生成
  • Gobang2DGameManager:核心游戏逻辑管理,处理落子、胜负判定、UI 交互等
二、核心流程架构
1. 棋盘初始化流程

1. 加载棋盘背景图像,设置RectTransform尺寸为(cellSize*(boardSize-1))x(cellSize*(boardSize-1))

2. 生成水平和垂直网格线:

- 水平线:从顶部到底部,共boardSize条,每条线横跨棋盘

- 垂直线:从左到右,共boardSize条,每条线纵贯棋盘

- 坐标计算:以棋盘中心为原点,通过(cellSize * 索引 - 中心偏移量)确定线端点位置

3. 创建可交互单元格:

- 每个单元格绑定点击事件,映射二维坐标(row, col)

- 单元格位置与网格线对齐,形成15x15的交互网格

2. 游戏逻辑流程

1. 初始化游戏状态:

- 棋盘数据gameBoard初始化为0(空),currentPlayer=1(黑棋先手)

- 重置时间、步数等统计数据,隐藏胜利面板

2. 落子交互流程:

- 点击单元格触发OnCellClicked,检查位置合法性(游戏进行中且空位)

- 更新棋盘数据,记录落子历史,生成对应颜色的棋子

- 执行胜利检测,若五连子则高亮获胜棋子并显示胜利面板

- 否则检查平局,若棋盘已满则显示平局面板

- 切换玩家回合,更新UI状态

3. 事件响应流程:

- 悔棋:回退最后一步,清空棋盘位置、销毁棋子、恢复玩家状态

- 重新开始:重置所有游戏数据,重新初始化棋盘

三、技术难点与解决方案
1. 二维坐标系统转换

难点:Unity UI 坐标与棋盘逻辑坐标的映射(UI 以左上角为原点,棋盘逻辑以中心为原点)

解决方案

  • 棋盘中心偏移量计算:centerOffset = (boardSize - 1) * cellSize / 2
  • 单元格位置计算:

// 逻辑坐标(row, col)转UI坐标

float xPos = col * cellSize - centerOffset;

float yPos = centerOffset - row * cellSize;

  • 实现棋盘居中布局,确保逻辑坐标与视觉位置一致
2. 五连子高效检测算法

难点:如何快速检测四个方向(水平、垂直、对角线、反对角线)的五连子

解决方案

  • 方向向量定义:int[,] directions = {{1,0}, {0,1}, {1,1}, {1,-1}}
  • 双向检测策略:
    1. 从落子点沿方向正向检查(+dx, +dy)
    1. 从落子点沿方向反向检查(-dx, -dy)
    1. 累计同色棋子数,若≥5 则判定胜利
  • 边界条件处理:检查坐标是否越界(0 ≤ row/col < BOARD_SIZE)
3. 游戏状态管理与回滚

难点:悔棋功能需要准确回退棋盘状态、棋子显示和玩家回合

解决方案

  • 落子历史记录:使用List存储每一步的 (row, col, player)
  • 状态回滚步骤:
    1. 清空棋盘对应位置(gameBoard [row, col] = 0)
    1. 销毁 UI 棋子对象(pieces [row, col])
    1. 移除历史记录最后一项(moveHistory.RemoveAt)
    1. 恢复上一玩家回合(currentPlayer = lastMove.Player)
4. 视觉效果与交互反馈

难点:落子动画和胜利高亮需要流畅的视觉体验

解决方案

  • 落子动画:使用协程实现棋子缩放动画(ScalePiece方法),通过 Lerp 插值平滑过渡
  • 胜利高亮:遍历获胜方向所有棋子,修改 Image 颜色为黄色(HighlightWinningPieces)
  • 交互反馈:单元格点击时通过 UI 状态更新(文字提示、玩家回合切换)增强体验
四、关键技术点总结
  1. UI 布局技术
    • 使用 RectTransform.anchoredPosition 实现棋盘元素的精确布局
    • LineRenderer 组件绘制网格线,通过 SetPosition 设置端点坐标
  1. 数据结构设计
    • 二维数组int[,] gameBoard存储棋盘状态(0 = 空,1 = 黑棋,2 = 白棋)
    • Move类封装落子信息,List管理历史记录
  1. 事件驱动机制
    • 单元格点击事件绑定(Button.onClick.AddListener)
    • UI 按钮事件响应(重新开始、悔棋功能)
  1. 性能优化点
    • 胜利检测仅检查落子点周围方向,避免全局扫描
    • 棋子对象池复用(可扩展优化方向)
五、扩展与优化方向
  1. 功能扩展
    • 增加 AI 对战模式(基于极大极小算法或启发式搜索)
    • 实现网络对战功能(使用 Photon 等网络框架)
    • 添加音效和更多动画效果(落子音效、胜利特效)
  1. 性能优化
    • 棋子对象池化管理,减少 Instantiate/Destroy 开销
    • 胜利检测算法优化(位运算或哈希表加速)
  1. UI 优化
    • 添加棋盘主题切换(木质、石质等皮肤)
    • 实现移动端触摸操作适配(点击区域扩大、拖拽落子)

    • using UnityEngine;
      using UnityEngine.UI;

      ///


      /// 负责在Unity中创建和配置2D棋盘及其网格线
      ///

      public class Board2DSetup : MonoBehaviour
      {
          [Header("棋盘设置")]
          public Image boardImage;           // 棋盘背景图像组件
          public Sprite boardSprite;         // 棋盘背景精灵
          public float cellSize = 40f;       // 每个格子的大小(像素)
          public int boardSize = 15;         // 棋盘网格尺寸(行列数)

          [Header("网格线设置")]
          public LineRenderer gridLinePrefab; // 网格线预制体

          void Start()
          {
              // 初始化棋盘
              CreateBoard();
              // 创建网格线
              CreateGridLines();
          }

          ///


          /// 设置棋盘背景图像的大小和精灵
          ///

          void CreateBoard()
          {
              // 获取棋盘图像的矩形变换组件
              RectTransform rectTransform = boardImage.GetComponent();

              // 计算棋盘总尺寸(根据格子大小和数量)
              float totalSize = cellSize * (boardSize - 1);

              // 设置棋盘图像尺寸
              rectTransform.sizeDelta = new Vector2(totalSize, totalSize);

              // 应用棋盘精灵
              boardImage.sprite = boardSprite;
          }

          ///


          /// 创建棋盘的水平和垂直网格线
          ///

          void CreateGridLines()
          {
              // 计算棋盘中心偏移量
              float centerOffset = (boardSize - 1) * cellSize / 2;

              // 创建水平线
              for (int i = 0; i < boardSize; i++)
              {
                  // 实例化网格线预制体
                  LineRenderer line = Instantiate(gridLinePrefab, transform);
                  line.positionCount = 2; // 设置线的端点数量

                  // 计算当前水平线的Y坐标(从上到下)
                  float yPos = centerOffset - i * cellSize;

                  // 设置水平线的起点和终点
                  line.SetPosition(0, new Vector3(-centerOffset, yPos, 0));
                  line.SetPosition(1, new Vector3(centerOffset, yPos, 0));
              }

              // 创建垂直线
              for (int i = 0; i < boardSize; i++)
              {
                  // 实例化网格线预制体
                  LineRenderer line = Instantiate(gridLinePrefab, transform);
                  line.positionCount = 2; // 设置线的端点数量

                  // 计算当前垂直线的X坐标(从左到右)
                  float xPos = i * cellSize - centerOffset;

                  // 设置垂直线的起点和终点
                  line.SetPosition(0, new Vector3(xPos, centerOffset, 0));
                  line.SetPosition(1, new Vector3(xPos, -centerOffset, 0));
              }
          }
      }

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

    ///


    /// 五子棋游戏核心管理类,负责游戏逻辑、状态管理和UI交互
    ///

    public class Gobang2DGameManager : MonoBehaviour
    {
        // 游戏常量定义
        public const int BOARD_SIZE = 15;  // 定义15x15的标准棋盘大小
        public float cellSize = 40f;       // 单个棋盘格子的物理尺寸(像素)

        // 游戏状态数据
        private int[,] gameBoard = new int[BOARD_SIZE, BOARD_SIZE];  // 棋盘数据存储(0=空,1=黑棋,2=白棋)
        private int currentPlayer = 1;       // 当前落子玩家(1=黑棋,2=白棋)
        private bool gameActive = true;      // 游戏是否正在进行
        private List moveHistory = new List();  // 落子历史记录
        private float gameTime = 0f;         // 游戏持续时间计时

        // UI组件引用
        public RectTransform boardParent;            // 棋盘父容器(用于定位单元格)
        public GameObject cellPrefab;                // 棋盘格子预制体
        public TextMeshProUGUI statusText;           // 游戏状态文本
        public TextMeshProUGUI currentPlayerText;    // 当前玩家文本
        public TextMeshProUGUI timeText;             // 游戏时间文本
        public TextMeshProUGUI moveCountText;        // 步数统计文本
        public Button restartButton;                 // 重新开始按钮
        public Button undoButton;                    // 悔棋按钮
        public GameObject winPanel;                  // 胜利面板
        public TextMeshProUGUI winnerText;           // 获胜者文本

        // 棋子预制体
        public GameObject blackPiecePrefab;  // 黑棋棋子预制体
        public GameObject whitePiecePrefab;  // 白棋棋子预制体

        // 游戏对象数组
        private GameObject[,] cells = new GameObject[BOARD_SIZE, BOARD_SIZE];   // 棋盘格子对象数组
        private GameObject[,] pieces = new GameObject[BOARD_SIZE, BOARD_SIZE];  // 棋子对象数组

        void Start()
        {
            // 游戏初始化
            InitializeGame();
        }

        void Update()
        {
            // 游戏进行中时更新计时
            if (gameActive)
            {
                gameTime += Time.deltaTime;
                UpdateTimeDisplay();
            }
        }

        ///


        /// 初始化游戏状态,包括棋盘、UI和玩家信息
        ///

        void InitializeGame()
        {
            // 清空棋盘数据
            for (int i = 0; i < BOARD_SIZE; i++)
            {
                for (int j = 0; j < BOARD_SIZE; j++)
                {
                    gameBoard[i, j] = 0;  // 0表示空位

                    // 销毁已存在的单元格和棋子
                    if (cells[i, j] != null) Destroy(cells[i, j]);
                    if (pieces[i, j] != null) Destroy(pieces[i, j]);

                    // 创建新的棋盘单元格
                    CreateCell(i, j);
                }
            }

            // 重置游戏状态
            currentPlayer = 1;          // 黑棋先手
            gameActive = true;          // 游戏激活
            moveHistory.Clear();        // 清空历史记录
            gameTime = 0f;              // 重置游戏时间

            // 更新UI显示
            UpdateGameStatus();
            UpdateTimeDisplay();

            // 隐藏胜利面板
            winPanel.SetActive(false);
        }

        ///


        /// 创建单个棋盘单元格并设置交互事件
        ///

        /// 单元格行索引
        /// 单元格列索引
        void CreateCell(int row, int col)
        {
            // 实例化单元格预制体
            GameObject cell = Instantiate(cellPrefab, boardParent);

            // 计算单元格在棋盘上的位置(居中布局)
            Vector2 position = new Vector2(
                col * cellSize - (BOARD_SIZE - 1) * cellSize / 2,  // X坐标(从左到右)
                (BOARD_SIZE - 1) * cellSize / 2 - row * cellSize   // Y坐标(从上到下)
            );

            // 设置单元格位置
            cell.GetComponent().anchoredPosition = position;
            cell.name = $"Cell_{row}_{col}";  // 命名便于调试

            // 添加点击事件处理
            Button cellButton = cell.GetComponent

你可能感兴趣的:(unity,游戏,游戏引擎)