You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity 2D国际象棋开发:升变阶段实现游戏暂停功能求助

2D国际象棋兵升变功能的暂停问题

我用Unity 2022.3.7f1开发2D国际象棋,正在实现兵的升变功能。已经制作了带按钮的Canvas,供玩家选择兵要升变的棋子。当前遇到的问题是:希望在玩家选择升变棋子期间暂停游戏,待选择完成后恢复游戏、隐藏Canvas并切换至对手回合。

现有逻辑是通过Figure脚本的OnMouseDown()调用Board脚本的MoveFigureClickControl(),移动和升变判断功能都正常,但WaitForPromotion()无法实现预期的暂停效果。尝试过将MoveFigureClickControl()改为IEnumerator并添加yield return WaitForPromotion(),但修改后无法点击棋子。

相关代码

Board脚本的MoveFigureClickControl()

public void MoveFigureClickControl(Figure figure, Tile tile)
{
    if (figureInHand == false && tile == null)
    {
        activeFigure = figure;
        figureInHand = true;
    }
    else if (figureInHand == true && activeFigure != null && legalMove != null)
    {
        if (legalMove.Contains(tile.Get()))
        {
            if (tile.transform.childCount > 1)
            {
                Object.Destroy(tile.transform.GetChild(1).gameObject);
            }
            activeFigure.transform.SetParent(tile.transform, false);
            activeFigure.GetComponent<Figure>().SetNewPosition();

            if (this.GetComponent<MoveHandler>().Promotion(activeFigure))
            {
                if (activeFigure.figureColor)
                {
                    Promotion_white.gameObject.SetActive(true);
                    inPromotion = true;
                    WaitForPromotion();
                }
                else
                {
                    Promotion_black.gameObject.SetActive(true);
                    inPromotion = true;
                    WaitForPromotion();
                }
            }

            figureInHand = false;
            activeFigure = null;
            ChangePlayer();
        }            
    }
}

Figure脚本的OnMouseDown()

void OnMouseDown()
{
    GetComponentInParent<Board>().MoveFigureClickControl(this, null);
}

Board脚本的WaitForPromotion()

private IEnumerable WaitForPromotion()
{
    while (inPromotion)
    {
        yield return null;
    }
}

升变按钮示例逻辑RookPromotion()

public void RookPromotion()
{
    (int, int) pawnPosition = activeFigure.GetCurrentPosition();
    string tileName = gameObject.GetComponent<MoveHandler>().GetTileName(pawnPosition.Item1, pawnPosition.Item2);
    Object.Destroy(gameObject.transform.Find(tileName).transform.GetChild(1).gameObject);
    GameObject newFigure;
    if (activeFigure.figureColor)
    {
        newFigure = Instantiate(rookPrefab_w);
        newFigure.transform.SetParent(gameObject.transform.Find(tileName), false);
        newFigure.GetComponent<Figure>().SetNewPosition();
    }
    else
    {
        newFigure = Instantiate(rookPrefab_b);
        newFigure.transform.SetParent(gameObject.transform.Find(tileName), false);
        newFigure.GetComponent<Figure>().SetNewPosition();
    }
    inPromotion = false;
}

问题根源

  1. WaitForPromotion()返回IEnumerable但未通过协程执行,直接调用不会触发暂停逻辑
  2. 将MoveFigureClickControl()改为协程后,OnMouseDown()直接调用协程方法不会启动协程,必须通过StartCoroutine()执行

修复方案

1. 把MoveFigureClickControl()改为协程

将返回类型改为IEnumerator,并在升变判断处用协程等待升变完成,同时调整后续逻辑的执行时机:

public IEnumerator MoveFigureClickControl(Figure figure, Tile tile)
{
    if (figureInHand == false && tile == null)
    {
        activeFigure = figure;
        figureInHand = true;
        yield break;
    }
    else if (figureInHand == true && activeFigure != null && legalMove != null)
    {
        if (legalMove.Contains(tile.Get()))
        {
            if (tile.transform.childCount > 1)
            {
                Object.Destroy(tile.transform.GetChild(1).gameObject);
            }
            activeFigure.transform.SetParent(tile.transform, false);
            activeFigure.GetComponent<Figure>().SetNewPosition();

            bool needPromotion = this.GetComponent<MoveHandler>().Promotion(activeFigure);
            if (needPromotion)
            {
                if (activeFigure.figureColor)
                {
                    Promotion_white.gameObject.SetActive(true);
                }
                else
                {
                    Promotion_black.gameObject.SetActive(true);
                }
                inPromotion = true;
                // 等待升变完成
                yield return StartCoroutine(WaitForPromotion());
            }

            figureInHand = false;
            activeFigure = null;
            ChangePlayer();
        }            
    }
}

2. 修正WaitForPromotion()的返回类型

Unity协程要求返回IEnumerator,修改后才能被正确识别:

private IEnumerator WaitForPromotion()
{
    while (inPromotion)
    {
        yield return null;
    }
}

3. 修改OnMouseDown()启动协程

直接调用协程方法不会执行,必须用StartCoroutine()启动:

void OnMouseDown()
{
    Board board = GetComponentInParent<Board>();
    StartCoroutine(board.MoveFigureClickControl(this, null));
}

4. 优化升变按钮逻辑(可选)

在升变完成后直接隐藏对应Canvas,避免后续操作受影响:

public void RookPromotion()
{
    (int, int) pawnPosition = activeFigure.GetCurrentPosition();
    string tileName = gameObject.GetComponent<MoveHandler>().GetTileName(pawnPosition.Item1, pawnPosition.Item2);
    Object.Destroy(gameObject.transform.Find(tileName).transform.GetChild(1).gameObject);
    GameObject newFigure;
    if (activeFigure.figureColor)
    {
        newFigure = Instantiate(rookPrefab_w);
        newFigure.transform.SetParent(gameObject.transform.Find(tileName), false);
        newFigure.GetComponent<Figure>().SetNewPosition();
        Promotion_white.gameObject.SetActive(false);
    }
    else
    {
        newFigure = Instantiate(rookPrefab_b);
        newFigure.transform.SetParent(gameObject.transform.Find(tileName), false);
        newFigure.GetComponent<Figure>().SetNewPosition();
        Promotion_black.gameObject.SetActive(false);
    }
    inPromotion = false;
}

核心说明

  • Unity协程必须通过StartCoroutine()启动,直接调用返回IEnumerator的方法不会生效
  • 暂停逻辑靠协程内的yield return null逐帧等待,直到inPromotion变为false
  • 升变完成后必须隐藏Canvas面板,避免干扰后续游戏操作

内容的提问来源于stack exchange,提问作者Teand

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 21:00:12