1.錯誤表現描述
抽卡時,10抽展示界面為A。抽取內容可能是整卡或者碎片,抽到整卡,會有立繪展示和點擊詳情的按鈕。
點擊詳情后出現詳情頁B。【此時界面A預制體被銷毀,卡片數據進入數據緩存池】
點擊頁面B的返回按鈕,單例的HuanLingRewardController讀取DataPool的內容加載界面。
Bug表現:10抽的抽卡展示界面沒有出現。
【P.S. 測試發現,問題在游戲引擎平臺上沒有任何問題,在真機中穩定復現】
2.錯誤排查階段
因為界面B沒有顯示,那么就可能有下面幾種情況
- 界面B的最大一級預制體沒有創建/或者被隱藏
- Grid下的每個Item可能沒有被創建成功/或者被隱藏
通過對指定卡片的GameObject進行addWatches 監視 和 對于SetActive位置進行斷點。
首先排除了,物品未被激活導致不顯示的問題。
然后從返回按鈕的點擊事件回調,追蹤了一下上下文結構。在機器適配過程中發現,代碼中并沒有專門為webgl編寫相關的宏分支。那么初步可以判斷問題不是出現在機型適配導致的。
接下來,我在CreateItem方法和HuanLingRewardController腳本的Awake和OnEnable階段斷點。
查看堆棧。追蹤一下上下文。
在繪制界面的必經方法中打印了日志(真機調試,用日志輸出)
然后。
對比正常創建10抽界面(第一次十抽后顯示)和 詳情頁返回10抽界面(Bug不顯示)的日志
上圖為正常加載界面,可以看到碎片都成功加載了。
上圖為不顯示界面的情況。發現HuanLingRewardController.awake 階段在showDrawResult后執行的。
看看awake做了什么事
private void Awake(){Debug.LogWarning("HuanLingRewardController.Awake()執行了");m_Instance = this;if (!CheckUI()) return;m_LiHuiView.PlayFinished = OnPlayFinished;m_LiHuiView.UpdateActiveTips = OnUpdateActiveTips;m_AutoActiveTipsTweeners = m_AutoActiveTips.GetComponents<UITweener>();m_AutoActiveTips.gameObject.SetActive(false);}
他對Instance實例初始化了。
因為實際上awake在后面執行,所以此時m_Instace == null ,然后在if (!m_Instance) return; 返回了
public static void ShowDrawResult(GC_SPIRITS_LOTTERY packet, bool playTweenAnimation = true){Debug.LogWarning("ShowDrawResult執行了");if (!m_Instance) return;m_Instance.InnerShowDrawResult(packet, playTweenAnimation);}
3.錯誤分析
那么為什么作為單例的Controller的awake階段會在他的靜態方法執行后才初始化。
之前的寫法是
public static void ShowLastReward(){Debug.LogWarning("ShowLastReward()執行了");if (m_LastSpiritsLotteryPacket == null) return;ShowUI(SubPage.Reward,true);HuanLingRewardController.ShowDrawResult(m_LastSpiritsLotteryPacket);}
如果ShowUI是同步加載資源的話,是沒有問題的但實際上showUI的基類會調用
private static bool DoShowUI(bool bSync, UIPathData pathData, OnOpenUIDelegate delOpenUI = null, object param = null)
將
AssetManager.LoadUI(pathData.path, m_instance.LoadUIBundleFinish, pathData);
//主要是調用了LoadUI
pathData.onOpenUI = delOpenUI;
pathData.param = param;
AssetManager.LoadUI(pathData.path, m_instance.LoadUIBundleFinish, pathData);
而LoadAsset是一個異步加載的原型
public static void LoadAsset(BundleType type, string name, Action<IAssetRef, object> callback, object param){if (string.IsNullOrEmpty(name))return;
#if USE_ABBundleTask task = new BundleTask(OnLoadRemoteAssetFinished);task.AddParam(param);task.AddParam(callback);task.Add(type, name);LoadBundle(task, AssetLoader.LoadQueueType.KEYRES);
#elseif (RemoteBundleManager.IsRemoteBundle(type, name)){BundleTask task = new BundleTask(OnLoadRemoteAssetFinished);task.AddParam(param);task.AddParam(callback);task.Add(type, name);LoadBundle(task, AssetLoader.LoadQueueType.KEYRES);}else if (Zeus.Framework.Asset.LocalAssetStatus.Ready == Zeus.Framework.Asset.AssetManager.GetAssetStatus(GetAssetPath(type, name), GetAssetType(type))){if (callback != null)callback(Zeus.Framework.Asset.AssetManager.LoadAsset(GetAssetPath(type, name), GetAssetType(type)), param);}else{Zeus.Framework.Asset.AssetManager.LoadAssetAsync(GetAssetPath(type, name), GetAssetType(type), callback, param);}
#endif}
說明代碼邏輯是先生成的窗口預制體然后,去緩存池中讀上一次抽卡保存的結果,然后加載數據刷新面板。
所以現在是異步加載未完成然后就調用了后續的靜態方法導致被迫中止。
4.問題解決
所以
HuanLingRewardController.ShowDrawResult(m_LastSpiritsLotteryPacket);
刷數據的邏輯應該放在ShowUI 執行成功的回調函數里。
于是,給方法新增標記,判斷是否是第二次加載
HuanLingRewardController.ShowDrawResult(m_LastSpiritsLotteryPacket, false);
ShowUI.cs中
public static void ShowUI(SubPage defaultPage,bool isSecondEnter = false){// 針對繪卷UI進行特殊處理if (StoryScrollMainView.GetInstance()){StoryScrollMainView.GetInstance().NeedShowMenu = false;}if (StoryScrollSubView.GetInstance()){StoryScrollSubView.GetInstance().NeedShowMenu = false;}UIManager.ShowUI(UIInfo.HuanLingRoot, (isSuccess, _) =>{if (isSuccess && Instance){Instance.OnShow(defaultPage);}if (isSecondEnter){HuanLingRewardController.ShowDrawResult(m_LastSpiritsLotteryPacket, false);}});}
在UIManager.ShowUI 執行成功的回調中加入上述代碼即可。
這樣就保證了在界面預制體加載完成后,才會走刷數據的流程。
出現這樣的問題也是因為,手機端異步加載資源的速度受到網絡延遲,服務器結點的影響很大。用同步加載的邏輯去思考異步功能肯定是不行的。