using System.Collections.Generic; using CoreGame; using GAS.Runtime; using UnityEngine; using xFrame; namespace CoreGame.Render { public struct TexiaoShowParam { public int effectId; // 静态配置 public int element; public Vector2 offset; public Quaternion rotation; public Vector3 scale; public override string ToString() { var tbRenderEffect = RenderEffectDescMgr.Instance.GetConfig(effectId); return "effectId: " + effectId + " name: " + tbRenderEffect?.name; } } public class TxShowProcessParam : ISptPool { public int effectId; public Vector2 offset; public Quaternion rotation; public Vector3 scale; public Vector2 ghostPos; // 若eid的实体已回收,使用此位置播放 public bool isAttach; public bool useHitPos; public ReuseGoData reuseGoData; public int eid; public void Awake(ref TexiaoShowParam param) { effectId = param.effectId; offset = param.offset; rotation = param.rotation; scale = param.scale; } public void Reset() { effectId = 0; offset = Vector2.zero; rotation = Quaternion.identity; scale = Vector3.one; ghostPos = Vector2.zero; isAttach = false; useHitPos = false; reuseGoData = null; eid = 0; } } [Combat] public class EffectShowProxy : ProxyBase { private readonly Dictionary effectListDict = new(); private readonly List m_CacheRenderers = new(); private readonly List m_CacheMaterials = new(); private int m_IsMatPushed = 0; public float effectScale = 1.0f; protected override void Initialize(GameObject obj, CombatEntity ent) { } public void ShowCastEffect(int effectId, List seqList = null) { TexiaoShowParam param = default; param.effectId = effectId; param.scale = Vector3.one; param.rotation = Quaternion.identity; param.offset = Vector2.zero; GA_ShowCastEffect(ref param, null, seqList); } public void ShowCastEffect(ref TexiaoShowParam param, in GameplayCueParameters cueData, List seqList = null) { if (cueData.sourceGaSpec != null) { GA_ShowCastEffect(ref param, cueData.sourceGaSpec.ctx, seqList); return; } GE_ShowCastEffect(ref param, cueData.sourceGeSpec, seqList); } public void GE_ShowCastEffect(ref TexiaoShowParam param, GameplayEffectSpec spec, List seqList = null) { Vector2 targetDir = Vector2.right; if (spec.Source != null && spec.Source.owner.IsValid()) { targetDir = spec.Owner.owner.transformProxy.position - spec.Source.owner.transformProxy.position; } var isShowSelf = IsShowSelf(ref param, seqList, out var path, out var isAttach, targetDir); if (isShowSelf == false) { XLog.LogWarning($"GESpec {spec.GameplayEffect.GameplayEffectName} -> {param}: 只能显示Owner特效"); return; } } public void GA_ShowCastEffect(ref TexiaoShowParam param, GameAbilityContext ctx, List seqList = null) { var targetDir = Vector2.zero; if (ctx != null) { var tbRenderEffect = RenderEffectDescMgr.Instance.GetConfig(param.effectId); if (tbRenderEffect.isLookAtTarget == VFXLookType.PointSelect && ctx.castPos.Count > 0) { targetDir = ctx.castPos[0].ToVector2() - ctx.ownerEnt.transformProxy.position; } else if (tbRenderEffect.isLookAtTarget == VFXLookType.CatchTarget && ctx.geCatchTargets.Count > 0) { for (int i = 0; i < ctx.geCatchTargets.Count; i++) { if (ctx.geCatchTargets[i].IsValid() && ctx.geCatchTargets[i].hasTransformProxy) { targetDir = ctx.geCatchTargets[i].transformProxy.position - ctx.ownerEnt.transformProxy.position; break; } } } } if (IsShowSelf(ref param, seqList, out var path, out var isAttach, targetDir)) return; if (ctx == null || ctx.geCatchTargets.Count == 0) { BattleLogger.LogInfo("ShowCastEffect: geCatchTargets is empty"); return; } for (var i = 0; i < ctx.geCatchTargets.Count; i++) { var useHitData = false; if (isAttach == false) useHitData = NormalParma(ref param, ctx.geCatchTargets[i].creationIndex, ctx); ShowSingleTargetEffect(ctx.geCatchTargets[i], path, useHitData, isAttach, ref param, seqList); } } private bool IsShowSelf(ref TexiaoShowParam param, List seqList, out string path, out bool isAttach, Vector2 targetDir) { seqList?.Clear(); var tbRenderEffect = RenderEffectDescMgr.Instance.GetConfig(param.effectId); if (tbRenderEffect == null) { BattleLogger.LogInfo($"ShowCastEffect: effectId {param.effectId} is not exist"); path = null; isAttach = false; return true; } if (tbRenderEffect.isScale != 0) // 需要乘上实体的缩放 param.scale *= effectScale; path = tbRenderEffect.prefab; if (param.element == (int)ElementType.Fire && !string.IsNullOrEmpty(tbRenderEffect.firePrefab)) path = tbRenderEffect.firePrefab; else if (param.element == (int)ElementType.Toxic && !string.IsNullOrEmpty(tbRenderEffect.toxicPrefab)) path = tbRenderEffect.toxicPrefab; else if (param.element == (int)ElementType.Thunder && !string.IsNullOrEmpty(tbRenderEffect.thunderPrefab)) path = tbRenderEffect.thunderPrefab; isAttach = tbRenderEffect.effectSpawn == EffectSpawn.Target; var isTargetSelf = tbRenderEffect.effectTarget == EffectTarget.Owner; if (isTargetSelf) { if (tbRenderEffect.isLookAtTarget != VFXLookType.None && tbRenderEffect.isLookAtTarget != VFXLookType.Default) { var rotation = Quaternion.LookRotation(Vector3.forward, targetDir.normalized); param.rotation = rotation; } ShowSingleTargetEffect(ent, path, false, isAttach, ref param, seqList); return true; } return false; } private bool NormalParma(ref TexiaoShowParam param, int targetEid, GameAbilityContext ctx) { if (ctx == null) return false; if (ctx.hitDatas.TryGetValue(targetEid, out var hitData)) { param.offset = hitData.hitPos; param.rotation = Quaternion.LookRotation(Vector3.forward, hitData.hitDir); return true; } return false; } private void ShowSingleTargetEffect(CombatEntity entity, string path, bool useHitData, bool isAttach, ref TexiaoShowParam param, List seqList = null) { if (entity == null || entity.isEnabled == false) { BattleLogger.LogInfo($"{entity} is null or not enabled"); return; } var txShowProcessParam = SptPool.Malloc(); txShowProcessParam.Awake(ref param); txShowProcessParam.isAttach = isAttach; txShowProcessParam.eid = entity.creationIndex; txShowProcessParam.useHitPos = useHitData; txShowProcessParam.ghostPos = entity.transformProxy.position; var pool = GoPoolDic.GetPool(path); var reuseGoData = pool.GetNewGo(ShowSingleTargetEffect, txShowProcessParam); txShowProcessParam.reuseGoData = reuseGoData; var seq = reuseGoData.GetSeq(); if (seqList != null) { seqList.Add(seq); } else { Contexts.Combat.renderTimerEntity?.renderTimer?.AddTimer(1.3f, seq, DestroyEffect); } effectListDict.Add(seq, txShowProcessParam); } private static void ShowSingleTargetEffect(ReuseGoData reuseGoData) { if (reuseGoData == null) return; var param = (TxShowProcessParam)reuseGoData.param; reuseGoData.param = null; // 只能被池管理 var effectObj = reuseGoData.go; var entity = Contexts.Combat.GetEntity(param.eid); // 此函数为回调函数,go一定有 if (param.isAttach && entity.IsValid()) { var tbRenderEffect = RenderEffectDescMgr.Instance.GetConfig(param.effectId); Transform bindPoint = entity.transformProxy.value; if (entity.bindPointProxy?.TryGetBindPointOrDefault(tbRenderEffect.effectPoint, out var point) == true) { bindPoint = point; } if (bindPoint == null) { XLog.LogDebug($"ShowSingleTargetEffect: bindPoint {tbRenderEffect.effectPoint} is invalid, effectId: {param.effectId}"); return; } effectObj.transform.SetParent(bindPoint); effectObj.transform.SetLocalTransform(param.offset, param.rotation, param.scale); } else if (param.useHitPos) { effectObj.transform.SetTransform(param.offset, param.rotation, param.scale); } else { // todo : 有问题,但应该不会出问题,需要矩阵转换 // 不要拿transform,可能没同步 var position = entity.IsValid() ? entity.transformProxy.position : param.ghostPos; effectObj.transform.SetTransform( new Vector3(position.x, position.y) + new Vector3(param.offset.x, param.offset.y), param.rotation, param.scale); } effectObj.SetActiveWrap(true); } public void ActivateEffect(List seqs) { for (int i = 0; i < seqs.Count; i++) { if (effectListDict.TryGetValue(seqs[i], out var tp)) { // 不能用warp if (!tp.reuseGoData.go) continue; tp.reuseGoData.go.SetActive(true); } } } public void DeactivateEffect(List seqs) { for (int i = 0; i < seqs.Count; i++) { if (effectListDict.TryGetValue(seqs[i], out var tp)) { if (!tp.reuseGoData.go) continue; tp.reuseGoData.go.SetActive(false); } } } public void DestroyEffect(List seqs) { for (var i = 0; i < seqs.Count; i++) { var seq = seqs[i]; if (effectListDict.TryGetValue(seq, out var tp)) { GoPoolDic.GetPool(tp.reuseGoData.path).ReleaseGo(tp.reuseGoData); SptPool.Free(ref tp); effectListDict.Remove(seq); } else { BattleLogger.LogInfo($"{ent} : DestroyEffect: seq {seq} is not exist"); } } } public void DestroyEffect(object sq) { var seq = (int)sq; if (effectListDict.TryGetValue(seq, out var tp)) { GoPoolDic.GetPool(tp.reuseGoData.path).ReleaseGo(tp.reuseGoData); SptPool.Free(ref tp); effectListDict.Remove(seq); } } #region Mat // 只能一次修改 public void PushMat(Material mat) { if (m_IsMatPushed < 0) m_IsMatPushed++; if (m_IsMatPushed > 0) return; m_IsMatPushed++; go.GetComponentsInChildren(m_CacheRenderers); for (var i = m_CacheRenderers.Count - 1; i >= 0; i--) { if (m_CacheRenderers[i].GetComponentInParent() != null) { m_CacheRenderers.RemoveAt(i); continue; } m_CacheMaterials.Add(m_CacheRenderers[i].sharedMaterial); m_CacheRenderers[i].sharedMaterial = mat; } } public List OpMats() { return m_CacheRenderers; } public void PopMat() { m_IsMatPushed--; for (var i = 0; i < m_CacheRenderers.Count; i++) { if (m_CacheRenderers[i]) { m_CacheRenderers[i].sharedMaterial = m_CacheMaterials[i]; } } m_CacheRenderers.Clear(); } #endregion protected override void OnReset() { effectScale = 1; } } }