Unity倒计时计时器重置Bug求助:实现每个RecipeTemplate独立计时
问题解决:Unity实现独立倒计时计时器
问题根源
你写的DeliveryManagerUI里的UptadeVisual(注意拼写错误,应为UpdateVisual)方法每次触发时,会销毁容器内所有已生成的recipe实例,再重新遍历所有等待中的RecipeSO生成新实例。这直接导致所有旧计时器UI被销毁重建,自然所有计时器都会被重置。
修复方案
核心思路是停止全删重建的逻辑,改为只同步新增和移除的条目,让每个UI实例保留独立的计时器状态。
1. 重构DeliveryManagerUI代码
添加一个字典来维护RecipeSO和对应UI实例的映射,精准跟踪需要添加或移除的UI:
using System.Collections; using System.Collections.Generic; using UnityEngine; public class DeliveryManagerUI : MonoBehaviour { [SerializeField] private Transform container; [SerializeField] private Transform recipeTemplate; // 存储每个Recipe对应的UI实例 private Dictionary<RecipeSO, Transform> recipeUIMap = new Dictionary<RecipeSO, Transform>(); private void Awake() { recipeTemplate.gameObject.SetActive(false); } private void Start() { DeliveryManager.Instance.OnRecipeSpawned += DeliveryManager_OnRecipeSpawned; DeliveryManager.Instance.OnRecipeCompeted += DeliveryManager_OnRecipeCompleted; // 初始化已有的等待订单UI InitializeExistingRecipes(); } private void InitializeExistingRecipes() { foreach (RecipeSO recipeSO in DeliveryManager.Instance.GetWaitingWave1RecipeSOList()) { CreateRecipeUI(recipeSO); } } private void DeliveryManager_OnRecipeSpawned(object sender, System.EventArgs e) { SyncRecipeUI(); } private void DeliveryManager_OnRecipeCompleted(object sender, System.EventArgs e) { SyncRecipeUI(); } // 同步UI与当前等待订单列表 private void SyncRecipeUI() { List<RecipeSO> currentWaitingRecipes = DeliveryManager.Instance.GetWaitingWave1RecipeSOList(); HashSet<RecipeSO> currentRecipeSet = new HashSet<RecipeSO>(currentWaitingRecipes); // 移除已完成订单的UI List<RecipeSO> toRemove = new List<RecipeSO>(); foreach (var kvp in recipeUIMap) { if (!currentRecipeSet.Contains(kvp.Key)) { Destroy(kvp.Value.gameObject); toRemove.Add(kvp.Key); } } foreach (RecipeSO recipe in toRemove) { recipeUIMap.Remove(recipe); } // 添加新增订单的UI foreach (RecipeSO recipeSO in currentWaitingRecipes) { if (!recipeUIMap.ContainsKey(recipeSO)) { CreateRecipeUI(recipeSO); } } } // 创建单个订单UI private void CreateRecipeUI(RecipeSO recipeSO) { Transform recipeTransform = Instantiate(recipeTemplate, container); recipeTransform.gameObject.SetActive(true); recipeTransform.GetComponent<DeliveryManagerSingleUI>().SetRecipeSO(recipeSO); recipeUIMap.Add(recipeSO, recipeTransform); } }
2. 优化DeliveryManager事件(推荐)
为了更高效,建议修改DeliveryManager的事件,让事件携带具体的RecipeSO,避免每次全量对比列表:
// 在DeliveryManager中定义自定义事件参数 public class RecipeSpawnedEventArgs : System.EventArgs { public RecipeSO RecipeSO { get; set; } } public class RecipeCompletedEventArgs : System.EventArgs { public RecipeSO RecipeSO { get; set; } } // 修改事件声明 public event System.EventHandler<RecipeSpawnedEventArgs> OnRecipeSpawned; public event System.EventHandler<RecipeCompletedEventArgs> OnRecipeCompeted; // 生成订单时触发事件 OnRecipeSpawned?.Invoke(this, new RecipeSpawnedEventArgs { RecipeSO = spawnedRecipe }); // 完成订单时触发事件 OnRecipeCompeted?.Invoke(this, new RecipeCompletedEventArgs { RecipeSO = completedRecipe });
然后更新DeliveryManagerUI的事件处理逻辑,直接处理单个订单:
private void DeliveryManager_OnRecipeSpawned(object sender, RecipeSpawnedEventArgs e) { CreateRecipeUI(e.RecipeSO); } private void DeliveryManager_OnRecipeCompleted(object sender, RecipeCompletedEventArgs e) { if (recipeUIMap.TryGetValue(e.RecipeSO, out Transform uiTransform)) { Destroy(uiTransform.gameObject); recipeUIMap.Remove(e.RecipeSO); } }
3. DeliveryManagerSingleUI无需大改
原代码的计时器逻辑没问题,现在每个UI实例只会被创建一次,SetRecipeSO也只会调用一次,计时器会保持独立运行。
关键说明
- 解决问题的核心是停止全删重建UI,改用映射关系跟踪每个订单对应的UI实例,只增删变化的条目,从而保留每个计时器的独立状态。
- 传递具体
RecipeSO的事件能大幅提升效率,避免不必要的列表对比操作。
内容的提问来源于stack exchange,提问作者Felix
相关产品推荐
相关产品推荐

