using System; using System.Collections.Generic; using BehaviorDesigner.Runtime; using Entitas; using UnityEngine; using xFrame; namespace CoreGame.Render.Service { public static class HybridCmptLoaderSrv { public static bool s_DebugModelCenter = true; public static Dictionary s_ProxyCmptDic = new() { { typeof(Transform), CombatComponentsLookup.TransformProxy }, { typeof(Animator), CombatComponentsLookup.AnimationProxy }, { typeof(JoystickRender), CombatComponentsLookup.JoystickProxy }, { typeof(BindPointRender), CombatComponentsLookup.BindPointProxy }, { typeof(EffectShowRender), CombatComponentsLookup.EffectShowProxy }, { typeof(BehaviorTree), CombatComponentsLookup.AI }, { typeof(Collider2D), CombatComponentsLookup.Collider2DProxy }, { typeof(NavRender), CombatComponentsLookup.NavAgentProxy }, }; public static List> s_ProxyCmpts = new List>() { new(typeof(Transform), CombatComponentsLookup.TransformProxy), new(typeof(Animator), CombatComponentsLookup.AnimationProxy), new(typeof(JoystickRender), CombatComponentsLookup.JoystickProxy), new(typeof(BindPointRender), CombatComponentsLookup.BindPointProxy), new(typeof(EffectShowRender), CombatComponentsLookup.EffectShowProxy), new(typeof(BehaviorTree), CombatComponentsLookup.AI), new(typeof(Collider2D), CombatComponentsLookup.Collider2DProxy), new(typeof(NavRender), CombatComponentsLookup.NavAgentProxy), }; public static void FillInProxyCmpt(this CombatEntity e, GameObject go, List ignores) { for (int i = 0; i < s_ProxyCmpts.Count; i++) { var type = s_ProxyCmpts[i]; if (ignores != null && ignores.Contains(type.Item1)) continue; LoadProxyCmpt(e, go, type.Item1, type.Item2); } } private static void LoadProxyCmpt(this CombatEntity e, GameObject go, Type handleType, int componentIndex) { if (e.HasComponent(componentIndex)) { var component = e.GetComponent(componentIndex); var proxy = (ProxyBase)component; if (proxy.hasInit) BattleLogger.LogInfo("entity already has component: {0}", component); proxy.Init(go, e); return; } var cmpt = go.GetComponentsInChildren(handleType); if (cmpt != null && cmpt.Length > 0) { var component = e.CreateComponent(componentIndex, CombatComponentsLookup.componentTypes[componentIndex]); ((ProxyBase)component).Init(go, e); e.AddComponent(componentIndex, component); } } public static void LoadAsset(this CombatEntity e) { LoadMainAsset(e); } public static void LoadMainAsset(this CombatEntity e) { var assetCmpt = e.asset; e.OnComponentRemoved -= OnComponentRemoved; e.OnComponentRemoved += OnComponentRemoved; ref var renderAsset = ref assetCmpt.mainAssetParam; if (renderAsset.scaleParam == 0) renderAsset.scaleParam = 1; var pool = GoPoolDic.GetPool(renderAsset.path); // 需要在实体销毁时回收 var go = pool.GetNewGo(MainAssetCallBack, e.creationIndex); renderAsset.reuseGoData = go; } private static void MainAssetCallBack(ReuseGoData reuseGoData) { var e = Contexts.Combat.GetEntity(reuseGoData.interlockInd); if (e == null || !e.IsValid) { // MainAsset 的回收在entity销毁时 // GoPoolDic.GetPool(reuseGoData.path).ReleaseGo(reuseGoData); XLog.LogDebug("entity is be destroyed"); return; } var assetCmpt = e.asset; ref var renderAsset = ref assetCmpt.mainAssetParam; var go = reuseGoData.go; if (go == null) { XLog.LogWarning($"go is null, asset path: {reuseGoData.path}"); return; } renderAsset.reuseGoData = reuseGoData; // 浪费一个赋值,不然函数内用到会空 if (!go.GetComponent()) go.AddComponent(); FillInProxyCmpt(e, go, assetCmpt.ignoreTypes); var find = GameObject.Find(renderAsset.parentNodeName); if (find != null) { go.transform.SetParent(find.transform, false); } if (s_DebugModelCenter) { var gameObject = GameObject.CreatePrimitive(PrimitiveType.Sphere); gameObject.transform.SetParent(go.transform, false); gameObject.transform.localScale = Vector3.one * 0.3f; } go.transform.localScale = renderAsset.scaleParam * Vector3.one; reuseGoData.go.transform.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity); // 加标识符 if (e.hasTransformProxy) { e.transformProxy.OnAssetLoaded(); go.SetActiveWrap(true); } else { go.SetActiveWrap(false); } reuseGoData.go.ClearTrail(); LoadMainPartAsset(e); e.isMainAssetLoaded = true; // 设置主体动画 if (!e.hasBlackboard || !e.hasBindPointProxy) return; var anim = e.animationProxy; BlackboardSrv.GetMainSkinParam(e.creationIndex, out var skinParam); SubAssetData subAssetData = SptPool.Malloc(); subAssetData.Awake(e.creationIndex, skinParam.path, skinParam.bpType, reuseGoData); anim.PushMainAnimancer(subAssetData, skinParam); SptPool.Free(ref subAssetData); e.animationProxy?.Play(FNameLookup.Born); if (renderAsset.hideStateBar) return; // state bar 依赖主体 LoadStateBarAsset(e); } private static void LoadMainPartAsset(this CombatEntity e) { var assetComponent = e.asset; while (assetComponent.needLoadSubAssets.TryDequeue(out var ret)) { // ret 游离 assetComponent.PushSubAsset(ref ret); ret.reuseGoData = e.GetNewGo(ret.path.GetDisplayString(), LoadMainPartAssetCallBack, ret); } } private static void LoadMainPartAssetCallBack(ReuseGoData reuseGoData) { var subAssetData = (SubAssetData)reuseGoData.param; var e = Contexts.Combat.GetEntity(subAssetData.ownerEid); if (e == null || !e.IsValid) { XLog.LogDebug("entity is be destroyed"); return; } var bp = e.bindPointProxy; Transform bindPoint = null; bp?.TryGetBindPointOrDefault(subAssetData.bindPointType, out bindPoint); if (bindPoint == null) { bindPoint = e.asset.mainAssetParam.reuseGoData.go.transform; } for (int i = 0; i < subAssetData.fillEntTypes.Count; i++) { LoadProxyCmpt(e, reuseGoData.go, subAssetData.fillEntTypes[i], s_ProxyCmptDic[subAssetData.fillEntTypes[i]]); } reuseGoData.go.transform.SetParent(bindPoint, false); reuseGoData.go.SetActiveWrap(true); reuseGoData.go.ClearTrail(); } private static void LoadStateBarAsset(this CombatEntity e) { if (!e.hasBlackboard || !e.hasBindPointProxy) return; var stateBarPath = string.Empty; if (e.isHero) { stateBarPath = "Model/StateBar/HeroStateBar"; } else if (e.isPartner) { stateBarPath = "Model/StateBar/MonsterStateBar"; } else if (e.hasMonster) { if (e.monster.monsterType == MonsterType.Intruder) { stateBarPath = "Model/StateBar/HeroStateBar"; } else if (e.monster.monsterType == MonsterType.Boss) { return; } else { stateBarPath = "Model/StateBar/MonsterStateBar"; } } e.GetNewGo(stateBarPath, reuseGoData => { var e2 = Contexts.Combat.GetEntity(reuseGoData.interlockInd); if (e2 == null || !e2.IsValid) { GoPoolDic.GetPool(reuseGoData.path).ReleaseGo(reuseGoData); XLog.LogDebug("entity is be destroyed"); return; } var bp = e2.bindPointProxy; var stateBarTrans = bp?.GetBindPoint(BindPointType.StateBar); if (!stateBarTrans) { GoPoolDic.GetPool(reuseGoData.path).ReleaseGo(reuseGoData); XLog.LogWarning("stateBar has no bind point"); return; } reuseGoData.go.transform.SetParent(stateBarTrans, false); reuseGoData.go.transform.localPosition = Vector3.zero; reuseGoData.go.transform.rotation = Quaternion.identity; reuseGoData.go.SetActiveWrap(true); SubAssetData subAssetData = SptPool.Malloc(); subAssetData.Awake(e2.creationIndex, stateBarPath, BindPointType.StateBar, reuseGoData); e2.asset.PushSubAsset(ref subAssetData); LoadProxyCmpt(e2, bp.go, typeof(StateBarRender), CombatComponentsLookup.StateBarProxy); }, e.creationIndex); } // 非通用需求 private static void LoadPartBodyAsset(this CombatEntity e) { if (!e.hasBlackboard || !e.hasBindPointProxy) return; var bd = e.blackboard; var bp = e.bindPointProxy; // 0是主体 for (int i = 1; i < bd.showCfgIds.Length; i++) { if (!BlackboardSrv.GetPartSkinParam(bd.showCfgIds[i], out var skinParam)) { BattleLogger.LogInfo("LoadPartBodyAsset not find skinParam {0} {1}", bd.cfgType, bd.showCfgIds[0]); continue; } var bindPoint = bp.GetBindPoint(skinParam.bpType); // 只有主角直接捕获 e.GetNewGo(skinParam.path.GetDisplayString(), (reuseGoData => { var e2 = Contexts.Combat.GetEntity(reuseGoData.interlockInd); if (e2 == null || !e2.IsValid) { GoPoolDic.GetPool(reuseGoData.path).ReleaseGo(reuseGoData); XLog.LogDebug("entity is be destroyed"); return; } reuseGoData.go.transform.SetParent(bindPoint, false); SubAssetData subAssetData = SptPool.Malloc(); subAssetData.Awake(e2.creationIndex, skinParam.path, skinParam.bpType, reuseGoData); e2.asset.PushSubAsset(ref subAssetData); e2.animationProxy.PushPartAnimancer(subAssetData, skinParam); }), e.creationIndex); // combine bind point } } public static void OnComponentRemoved(IEntity entity, int index, IComponent component) { if (index == CombatComponentsLookup.Collider2DProxy) { ((Collider2DProxy)component).OnComponentRemoved(); } } } }