Unity成就系统中C# Coroutine仅触发一次的问题求助
我正在做学校项目的Cookie Clicker类型游戏,现在完善成就系统。获得成就时往achievementlist列表加整数,系统每6秒检查列表项数,项数≥1就运行协程播放动画。但实际运行有两个问题:
- 直接跳转到列表最新项,而非第一项
- 打印显示列表还有项,但动画只播放一次;播放后用
achievementlist.RemoveAt(0),之前列表有两个值,剩余一个后就被忽略了
问题代码
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class Achievements : MonoBehaviour { public int isrunning = 1; public bool checkcomplete = false; [SerializeField] GameObject achievementBanner; [SerializeField] GameObject achievementText; [SerializeField] GameObject achievementBannerAnimation; [SerializeField] GameObject achievementTextAnimation; [SerializeField] GameObject achiOne; [SerializeField] GameObject achiTwo; [SerializeField] GameObject achiThree; [SerializeField] GameObject achiFour; [SerializeField] GameObject achiFive; [SerializeField] GameObject achiSix; ///[SerializeField] GameObject[] AchievementList; public List<int> achievementlist = new List<int> (); ///public int[] achievementsunlock = new int[]; [SerializeField] public static int achiementcount; bool achievementrec = false; ///bool achievementani = false; bool achidone = false; // Start is called before the first frame update void Start() { achiOne.SetActive(false); achiTwo.SetActive(false); achiThree.SetActive(false); achiFour.SetActive(false); achiFive.SetActive(false); achiSix.SetActive(false); achievementBanner.SetActive(false); achievementText.SetActive(false); achievementText.GetComponent<Text>().text = "Achievement: First cookie!"; } // Update is called once per frame void Update() { print("|"); print(achievementlist.Count); print("|"); if (isrunning == 1) { StartCoroutine(Wait()); } if (checkcomplete == true) { StartCoroutine(CheckAchievement()); } ///print(achievementlist.Count); ///print(achidone); if (achiementcount == 1) { ///print("Test D"); ///achiOne.SetActive(true); ///achievementBanner.SetActive(true); ///achievementText.SetActive(true); if (!achidone) { ///print("Test D"); achiOne.SetActive(true); ///achievementBanner.SetActive(true); ///achievementText.SetActive(true); if (!achievementrec) { achievementlist.Add(1); achievementrec = true; } } ///print(achievementlist[1]); ///print(achievementlist[1]); ///StartCoroutine(AchievementGot()); ///achidone = true; ///} ///} } if (achiementcount == 2) { achievementText.GetComponent<Text>().text = "Achievement: First Clicker!"; ///achievementrec = false; if (achievementrec == true) { achievementlist.Add(2); achievementrec = false; } ///print(achievementlist[1]); ///print(achievementlist[2]); ///print("Test C"); achiTwo.SetActive(true); ///achievementBanner.SetActive(true); ///achievementText.SetActive(true); ///StartCoroutine(AchievementGot()); } ///print(achidone); ///achidone = false; if (achiementcount == 3) { achievementText.GetComponent<Text>().text = "Achievement: First Upgrade!"; achievementlist.Add(3); ///achievementrec = false; ///print("Test A"); achiThree.SetActive(true); ///achievementBanner.SetActive(true); ///achievementText.SetActive(true); ///StartCoroutine(AchievementGot()); ///} ////} } } public IEnumerator Wait() { isrunning = 0; yield return new WaitForSeconds(7); if (achievementlist.Count >= 1) { achievementlist.RemoveAt(0); print("Test A"); print(achievementlist.Count); checkcomplete = true; } else { print("Test B"); checkcomplete = false; } isrunning = 1; } IEnumerator CheckAchievement() { ///achievementlist.RemoveAt(0); achievementBanner.SetActive(true); achievementText.SetActive(true); ///achievementText.GetComponent<Text>().text = (achievementlist[1]); achievementBannerAnimation.GetComponent<Animation>().Play("Achievement"); achievementTextAnimation.GetComponent<Animation>().Play("AchiText"); ///achievementlist.RemoveAt(0); yield return new WaitForSeconds(6); achievementBanner.SetActive(false); achievementText.SetActive(false); ///yield return new WaitForSeconds(6); } IEnumerator AchievementGot() { ///achievementrec = true; ///achievementlist.RemoveAt(1); ///if (achievementlist.Count == 1) ///{ ///print("Test List"); ///achievementBannerAnimation.GetComponent<Animation>().Play("Achievement"); ///achievementTextAnimation.GetComponent<Animation>().Play("AchiText"); ///achievementlist.RemoveAt(0); ///achidone = true; ///} ///achievementBannerAnimation.GetComponent<Animation>().Play("Achievement"); ///achievementTextAnimation.GetComponent<Animation>().Play("AchiText"); ///print(achievementlist[1]); ///print(achievementlist[2]); yield return new WaitForSeconds(6); achidone = true; ///print(achievementlist.Contains(1)); ///print(achievementlist.Count); ///if(achievementlist.Length >= 1) ///{ ///print("Test F"); ///} ///print("Test B"); achievementBanner.SetActive(false); achievementText.SetActive(false); ///achievementrec = true; } }
问题根源与修复方案
1. 列表项被提前移除导致数据丢失
Wait协程里在触发动画前就执行了achievementlist.RemoveAt(0),直接删掉第一个成就,后续动画拿不到对应数据,多成就场景下还会跳过剩余项。
修复:把移除操作移到动画播放完成后,放在CheckAchievement协程末尾,同时根据当前成就ID设置对应文本:
IEnumerator CheckAchievement() { // 获取当前要显示的成就ID int currentAchievementId = achievementlist[0]; // 根据ID匹配对应文本 switch(currentAchievementId) { case 1: achievementText.GetComponent<Text>().text = "Achievement: First cookie!"; break; case 2: achievementText.GetComponent<Text>().text = "Achievement: First Clicker!"; break; case 3: achievementText.GetComponent<Text>().text = "Achievement: First Upgrade!"; break; } achievementBanner.SetActive(true); achievementText.SetActive(true); achievementBannerAnimation.GetComponent<Animation>().Play("Achievement"); achievementTextAnimation.GetComponent<Animation>().Play("AchiText"); yield return new WaitForSeconds(6); achievementBanner.SetActive(false); achievementText.SetActive(false); // 动画播放完成后再移除第一项 achievementlist.RemoveAt(0); // 重置标记,等待下一轮检查 checkcomplete = false; }
2. 协程重复触发导致逻辑混乱
Update每帧检查isrunning == 1就启动Wait协程,会生成多个并行协程,互相干扰状态值。
修复:用布尔值标记协程运行状态,避免重复启动:
// 替换原int类型的isrunning为布尔值 private bool isWaitCoroutineRunning = false; void Update() { // ...其他代码... if (!isWaitCoroutineRunning) { StartCoroutine(Wait()); } // ...其他代码... } public IEnumerator Wait() { isWaitCoroutineRunning = true; yield return new WaitForSeconds(6); // 改为需求的6秒检查间隔 if (achievementlist.Count >= 1 && !checkcomplete) { checkcomplete = true; } isWaitCoroutineRunning = false; }
3. 成就添加逻辑冗余且错误
原代码用achievementrec全局标记导致部分成就无法正确添加,改为给每个成就加独立标记,确保只添加一次:
// 给每个成就添加独立的已获取标记 private bool hasGotAchievement1 = false; private bool hasGotAchievement2 = false; private bool hasGotAchievement3 = false; void Update() { // ...其他代码... if (achiementcount == 1 && !hasGotAchievement1) { achiOne.SetActive(true); achievementlist.Add(1); hasGotAchievement1 = true; } if (achiementcount == 2 && !hasGotAchievement2) { achiTwo.SetActive(true); achievementlist.Add(2); hasGotAchievement2 = true; } if (achiementcount == 3 && !hasGotAchievement3) { achiThree.SetActive(true); achievementlist.Add(3); hasGotAchievement3 = true; } // ...其他代码... }
内容的提问来源于stack exchange,提问作者MS Paint
相关产品推荐
相关产品推荐

