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; }
问题根源
WaitForPromotion()返回IEnumerable但未通过协程执行,直接调用不会触发暂停逻辑- 将
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
相关产品推荐
相关产品推荐

