796 lines
28 KiB
C#
796 lines
28 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using UnityEngine;
|
||
using RuntimeData;
|
||
using Animancer;
|
||
using Logic;
|
||
using Logic.Audio;
|
||
using Logic.Skill;
|
||
using TH1Resource;
|
||
using TMPro;
|
||
using Unity.IO.LowLevel.Unsafe;
|
||
using Unity.VisualScripting;
|
||
using Object = UnityEngine.Object;
|
||
using PropertyAttribute = UnityEngine.PropertyAttribute;
|
||
using Random = UnityEngine.Random;
|
||
|
||
namespace TH1Renderer
|
||
{
|
||
public enum GridVFXType {
|
||
Heal = 0,
|
||
Die = 1,
|
||
DieHint = 2,
|
||
CounterDieHint = 3,
|
||
Flag = 4,
|
||
Damage = 5,
|
||
Hurt = 6,
|
||
Treasure = 7,
|
||
Fog = 8,
|
||
Coin = 9,
|
||
Fire =10,
|
||
ROYALFLAMES = 11,
|
||
//TewiFrenchBuff=12,
|
||
//KaguyaFrenchBuff=13,
|
||
CityConnect=14,
|
||
RedMistCreate=15,
|
||
//SakuyaGuard=16,
|
||
Lucky=17,
|
||
BigLucky=18,
|
||
Unlucky =19,
|
||
BigUnlucky=20,
|
||
LuckyText=21,
|
||
BigLuckyText=22,
|
||
UnluckyText=23,
|
||
BigUnluckyText=24,
|
||
Luxury=25,
|
||
KomeijiFear=26,
|
||
SkillIcon=27,
|
||
Dancing=28,
|
||
};
|
||
|
||
|
||
public class GridVFXManager
|
||
{
|
||
public Dictionary<GridVFXType, GridVFXRenderer> GridVFXDict;
|
||
|
||
public GridVFXManager(GameObject _effect)
|
||
{
|
||
GridVFXDict = new Dictionary<GridVFXType, GridVFXRenderer>();
|
||
if (!GridVFXRendererFactory.NewGridVFXDict(_effect, GridVFXDict))
|
||
Debug.LogError("Failed to create GridVFXDict");
|
||
}
|
||
|
||
public bool SetStop(GridVFXType type)
|
||
{
|
||
if (!GridVFXDict.TryGetValue(type, out var renderer)) return false;
|
||
renderer.SetStop();
|
||
return true;
|
||
}
|
||
|
||
public void Clear()
|
||
{
|
||
foreach (var obj in GridVFXDict)
|
||
{
|
||
obj.Value?.Clear();
|
||
}
|
||
}
|
||
|
||
public void Update()
|
||
{
|
||
foreach (var vfxPair in GridVFXDict)
|
||
vfxPair.Value?.Update();
|
||
}
|
||
|
||
public bool PlayVFX(GridVFXParams param,GridVFXPlayType playType)
|
||
{
|
||
if (!GridVFXDict.TryGetValue(param.Type, out var vfxRenderer))
|
||
{
|
||
//Debug.LogWarning($"[GridVFX][PlayVFX] ❌ NO RENDERER for type={param.Type}, playType={playType}. Dict has {GridVFXDict.Count} entries.");
|
||
return false;
|
||
}
|
||
if (vfxRenderer == null)
|
||
{
|
||
//Debug.LogWarning($"[GridVFX][PlayVFX] ❌ RENDERER NULL for type={param.Type}, playType={playType}");
|
||
return false;
|
||
}
|
||
if (playType is GridVFXPlayType.Play or GridVFXPlayType.PlayOnce)
|
||
{
|
||
//Debug.Log($"[GridVFX][PlayVFX] ▶️ PLAY type={param.Type}, playType={playType}, rendererClass={vfxRenderer.GetType().Name}, VFXObject={(vfxRenderer.VFXObject != null ? vfxRenderer.VFXObject.name : "NULL")}, VFXAnim={(vfxRenderer.VFXAnim != null ? vfxRenderer.VFXAnim.name : "NULL")}, Sprite={(vfxRenderer.Sprite != null ? vfxRenderer.Sprite.name : "NULL")}, IsPlaying(before)={vfxRenderer.IsPlaying}");
|
||
vfxRenderer.UpdateSpecialParam(param);
|
||
vfxRenderer.SetPlay(playType);
|
||
//Debug.Log($"[GridVFX][PlayVFX] ✅ AFTER SetPlay type={param.Type}, IsPlaying={vfxRenderer.IsPlaying}, VFXObject.activeSelf={(vfxRenderer.VFXObject != null ? vfxRenderer.VFXObject.activeSelf.ToString() : "NULL")}");
|
||
}
|
||
else
|
||
{
|
||
//Debug.Log($"[GridVFX][PlayVFX] ⏹️ STOP type={param.Type}");
|
||
vfxRenderer.SetStop();
|
||
}
|
||
|
||
return true;
|
||
}
|
||
}
|
||
public static class GridVFXRendererFactory
|
||
{
|
||
private static readonly Dictionary<GridVFXType, Func<GridVFXParams, GridVFXInfoDataAssets.GridVFXInfo, GridVFXRenderer>> _rendererFactory
|
||
= new Dictionary<GridVFXType, Func<GridVFXParams, GridVFXInfoDataAssets.GridVFXInfo, GridVFXRenderer>>();
|
||
|
||
private static bool _factoryInitialized;
|
||
|
||
public static void RegisterRenderer(GridVFXType type, Func<GridVFXParams, GridVFXInfoDataAssets.GridVFXInfo, GridVFXRenderer> creator)
|
||
{
|
||
if (creator == null) return;
|
||
_rendererFactory[type] = creator;
|
||
}
|
||
|
||
private static void EnsureFactoryInitialized()
|
||
{
|
||
if (_factoryInitialized) return;
|
||
|
||
RegisterRenderer(GridVFXType.Flag, (param, config) => new FlagGridVFXRenderer(param, config));
|
||
RegisterRenderer(GridVFXType.Damage, (param, config) => new DamageGridVFXRenderer(param, config));
|
||
RegisterRenderer(GridVFXType.ROYALFLAMES, (param, config) => new ROYALFLAMESGridVFXRenderer(param, config));
|
||
//RegisterRenderer(GridVFXType.TewiFrenchBuff, (param, config) => new TewiFrenchBuffGridVFXRenderer(param, config));
|
||
RegisterRenderer(GridVFXType.Coin, (param, config) => new CoinGridVFXRenderer(param, config));
|
||
RegisterRenderer(GridVFXType.SkillIcon, (param, config) => new SkillIconGridVFXRenderer(param, config));
|
||
RegisterRenderer(GridVFXType.Fire, (param, config) => new FireGridVFXRenderer(param));
|
||
RegisterRenderer(GridVFXType.Dancing, (param, config) => new DancingGridVFXRenderer(param, config));
|
||
|
||
_factoryInitialized = true;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 从配置创建 GridVFXRenderer
|
||
/// </summary>
|
||
public static GridVFXRenderer Create(GridVFXParams param)
|
||
{
|
||
EnsureFactoryInitialized();
|
||
|
||
// 尝试获取配置
|
||
if (Table.Instance?.GridVFXInfoDataAssets == null)
|
||
{
|
||
Debug.LogError("[GridVFXRendererFactory] GridVFXInfoDataAssets not loaded!");
|
||
return null;
|
||
}
|
||
|
||
if (!Table.Instance.GridVFXInfoDataAssets.GetGridVFXInfo(param.Type, out var vfxInfo))
|
||
{
|
||
Debug.LogWarning($"[GridVFXRendererFactory] No config found for GridVFXType: {param.Type}");
|
||
return null;
|
||
}
|
||
|
||
if (_rendererFactory.TryGetValue(param.Type, out var creator))
|
||
return creator(param, vfxInfo);
|
||
|
||
return new GenericGridVFXRenderer(param, vfxInfo);
|
||
}
|
||
|
||
public static bool NewGridVFXDict(GameObject _effect, Dictionary<GridVFXType, GridVFXRenderer> dict)
|
||
{
|
||
EnsureFactoryInitialized();
|
||
if (_effect == null || dict == null) return false;
|
||
|
||
var dataAssets = Table.Instance?.GridVFXInfoDataAssets;
|
||
if (dataAssets == null)
|
||
{
|
||
Debug.LogError("[GridVFXRendererFactory] GridVFXInfoDataAssets not loaded!");
|
||
return false;
|
||
}
|
||
|
||
// 遍历配置表中所有已配置的VFX类型
|
||
foreach (var vfxType in dataAssets.GetAllConfiguredTypes())
|
||
{
|
||
if (!dataAssets.GetGridVFXInfo(vfxType, out var vfxInfo))
|
||
{
|
||
//Debug.LogWarning($"[GridVFX][Factory] ⚠️ No GridVFXInfo for type={vfxType}");
|
||
continue;
|
||
}
|
||
|
||
// 查找 GameObject
|
||
GameObject vfxObject = null;
|
||
if (!string.IsNullOrEmpty(vfxInfo.GameObjectPath))
|
||
{
|
||
vfxObject = _effect.transform.Find(vfxInfo.GameObjectPath)?.gameObject;
|
||
}
|
||
|
||
if (vfxType == GridVFXType.Coin)
|
||
{
|
||
//Debug.Log($"[GridVFX][Factory] 🪙 Coin registration: GameObjectPath='{vfxInfo.GameObjectPath}', vfxObject={(vfxObject != null ? vfxObject.name : "NULL")}, AnimClip={(vfxInfo.AnimClip != null ? vfxInfo.AnimClip.name : "NULL")}, Sprite={(vfxInfo.Sprite != null ? vfxInfo.Sprite.name : "NULL")}");
|
||
}
|
||
|
||
// 创建参数(使用配置中的资源)
|
||
var param = new GridVFXParams(
|
||
vfxInfo.Type,
|
||
vfxInfo.AnimClip,
|
||
vfxInfo.Sprite,
|
||
vfxObject
|
||
);
|
||
|
||
var renderer = Create(param);
|
||
if (renderer != null)
|
||
{
|
||
dict[vfxType] = renderer;
|
||
}
|
||
else if (vfxType == GridVFXType.Coin)
|
||
{
|
||
//Debug.LogError("[GridVFX][Factory] ❌ Coin renderer creation returned null!");
|
||
}
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
}
|
||
|
||
public enum GridVFXPlayType {Play,Stop,PlayOnce}
|
||
public class GridVFXParams
|
||
{
|
||
// 通用
|
||
public GridVFXType Type;
|
||
public GameObject VFXObject;
|
||
public AnimationClip VFXAnim;
|
||
public Sprite Sprite;
|
||
|
||
// 特殊子类用
|
||
public uint CivId;
|
||
public int Damage;
|
||
public SkillType SkillType;
|
||
public UnitFullType UnitFullType;
|
||
|
||
//从gridData持有的GridVFXRenderMark结构,转化为GridVFXRenderer所需要的param结构
|
||
public GridVFXParams(GridVFXType type)
|
||
{
|
||
Type = type;
|
||
}
|
||
|
||
public GridVFXParams(GridVFXType type, int dmg)
|
||
{
|
||
if (type is GridVFXType.Damage)
|
||
{
|
||
Type = type;
|
||
Damage = dmg;
|
||
}
|
||
else
|
||
Debug.LogError("Wrong GridVFX Type");
|
||
}
|
||
|
||
public GridVFXParams(GridVFXType gridVFXType, AnimationClip vfxAnim, Sprite sprite, GameObject vfxObject,GridVFXPlayType playType = GridVFXPlayType.PlayOnce)
|
||
{
|
||
Type = gridVFXType;
|
||
VFXObject = vfxObject;
|
||
Sprite = sprite;
|
||
VFXAnim = vfxAnim;
|
||
Damage = 0;
|
||
CivId = 0;
|
||
SkillType = SkillType.NONE;
|
||
UnitFullType = new UnitFullType();
|
||
}
|
||
|
||
public GridVFXParams(GridVFXType type, SkillType skillType, UnitFullType unitFullType)
|
||
{
|
||
Type = type;
|
||
SkillType = skillType;
|
||
UnitFullType = unitFullType;
|
||
Damage = 0;
|
||
CivId = 0;
|
||
Sprite = null;
|
||
VFXAnim = null;
|
||
VFXObject = null;
|
||
}
|
||
|
||
public GridVFXParams(GridVFXType type, SkillType skillType, UnitFullType unitFullType, AnimationClip vfxAnim, Sprite sprite, GameObject vfxObject)
|
||
{
|
||
Type = type;
|
||
SkillType = skillType;
|
||
UnitFullType = unitFullType;
|
||
VFXAnim = vfxAnim;
|
||
Sprite = sprite;
|
||
VFXObject = vfxObject;
|
||
Damage = 0;
|
||
CivId = 0;
|
||
}
|
||
|
||
|
||
}
|
||
|
||
public abstract class GridVFXRenderer
|
||
{
|
||
public GridVFXType Type;
|
||
public AnimationClip VFXAnim;
|
||
public Sprite Sprite;
|
||
public GameObject VFXObject;
|
||
protected SpriteRenderer _renderer;
|
||
protected Transform _transfrom;
|
||
public bool IsPlaying;
|
||
|
||
// 配置引用(可选)
|
||
protected GridVFXInfoDataAssets.GridVFXInfo _config;
|
||
|
||
|
||
public GridVFXRenderer(GridVFXParams param)
|
||
{
|
||
Type = param.Type;
|
||
VFXAnim = param.VFXAnim;
|
||
Sprite = param.Sprite;
|
||
VFXObject = param.VFXObject;
|
||
IsPlaying = false;
|
||
_renderer = VFXObject != null ? VFXObject.GetComponent<SpriteRenderer>() : null;
|
||
_transfrom = VFXObject != null ? VFXObject.GetComponent<Transform>() : null;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 应用配置(仅 GenericGridVFXRenderer 会实际改变外观,特殊 Renderer 可覆盖此方法)
|
||
/// </summary>
|
||
protected virtual void ApplyVisualConfig()
|
||
{
|
||
if (_config == null || VFXObject == null) return;
|
||
|
||
if (_renderer != null)
|
||
{
|
||
_renderer.color = _config.TintColor;
|
||
}
|
||
|
||
// 只有明确设置了非 (1,1,1) 的 Scale 时才应用(保持 prefab 兼容性)
|
||
if (_transfrom != null && _config.Scale != Vector3.one)
|
||
{
|
||
#if UNITY_EDITOR
|
||
//Debug.Log($"[GridVFX] Applying scale {_config.Scale} to {Type}, current: {_transfrom.localScale}");
|
||
#endif
|
||
_transfrom.localScale = _config.Scale;
|
||
}
|
||
}
|
||
|
||
public virtual void SetPlay(GridVFXPlayType playType = GridVFXPlayType.PlayOnce)
|
||
{
|
||
if (IsPlaying)
|
||
{
|
||
//Debug.LogWarning($"[GridVFX][SetPlay-Base] ⚠️ SKIP (already playing) type={Type}");
|
||
return;
|
||
}
|
||
if (VFXObject == null)
|
||
{
|
||
//Debug.LogWarning($"[GridVFX][SetPlay-Base] ❌ VFXObject NULL type={Type}");
|
||
return;
|
||
}
|
||
if (Sprite != null && _renderer != null)
|
||
_renderer.sprite = Sprite;
|
||
Play(playType);
|
||
}
|
||
|
||
public virtual void Play(GridVFXPlayType playType = GridVFXPlayType.PlayOnce)
|
||
{
|
||
if (VFXObject == null)
|
||
{
|
||
//Debug.LogWarning($"[GridVFX][Play] ❌ VFXObject NULL for type={Type}");
|
||
return;
|
||
}
|
||
var vfxObject = VFXObject;
|
||
vfxObject.SetActive(true);
|
||
IsPlaying = true;
|
||
var animancer = vfxObject.GetComponent<AnimancerComponent>();
|
||
//Debug.Log($"[GridVFX][Play] 🎬 type={Type}, playType={playType}, VFXObject={VFXObject.name}, animancer={(animancer != null ? "OK" : "NULL")}, VFXAnim={(VFXAnim != null ? VFXAnim.name : "NULL")}");
|
||
if (VFXAnim != null && animancer != null)
|
||
{
|
||
animancer.Play(VFXAnim);
|
||
//Debug.Log($"[GridVFX][Play] 🎞️ animancer.Play called for type={Type} clip={VFXAnim.name} length={VFXAnim.length}");
|
||
//这里还要处理音频 TODO 将来配表化
|
||
if (Type == GridVFXType.Die)
|
||
{
|
||
AudioManager.Instance?.PlayAudio("SFX/UNIT_die");
|
||
}
|
||
if (Type == GridVFXType.Heal)
|
||
{
|
||
AudioManager.Instance?.PlayAudio("SFX/UNIT_heal");
|
||
}
|
||
if(playType is GridVFXPlayType.PlayOnce)
|
||
{
|
||
var timer = Timer.Instance;
|
||
if (timer == null)
|
||
{
|
||
if (vfxObject != null)
|
||
vfxObject.SetActive(false);
|
||
IsPlaying = false;
|
||
}
|
||
else
|
||
{
|
||
var animLength = VFXAnim.length;
|
||
timer.TimerRegister(vfxObject, () =>
|
||
{
|
||
if (vfxObject != null)
|
||
vfxObject.SetActive(false);
|
||
IsPlaying = false;
|
||
}, animLength,"GridVFXRenderer_" + Type);
|
||
}
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
public virtual void Clear()
|
||
{
|
||
IsPlaying = false;
|
||
}
|
||
|
||
public virtual void SetStop()
|
||
{
|
||
if (IsPlaying) Stop();
|
||
}
|
||
|
||
public virtual void Stop()
|
||
{
|
||
IsPlaying = false;
|
||
VFXObject?.SetActive(false);
|
||
}
|
||
|
||
public virtual void Update()
|
||
{
|
||
}
|
||
|
||
public virtual void UpdateSpecialParam(GridVFXParams param){}
|
||
|
||
public virtual void SetIntParam(int param)
|
||
{
|
||
|
||
}
|
||
};
|
||
|
||
|
||
public class FlagGridVFXRenderer : GridVFXRenderer
|
||
{
|
||
public uint CivId;
|
||
|
||
public FlagGridVFXRenderer(GridVFXParams param) : base(param)
|
||
{
|
||
CivId = param.CivId;
|
||
}
|
||
|
||
public FlagGridVFXRenderer(GridVFXParams param, GridVFXInfoDataAssets.GridVFXInfo config) : base(param)
|
||
{
|
||
CivId = param.CivId;
|
||
_config = config;
|
||
}
|
||
|
||
public override void SetPlay(GridVFXPlayType playType = GridVFXPlayType.PlayOnce)
|
||
{
|
||
if (IsPlaying) return;
|
||
if (VFXObject == null) return;
|
||
if (Table.Instance.PlayerDataAssets.GetPlayerInfoByCivId(CivId, CivId, out var playerInfo))
|
||
{
|
||
Sprite = playerInfo.FlagIcon;
|
||
var flagBgRenderer = VFXObject.transform.Find("FlagBG2")?.GetComponent<SpriteRenderer>();
|
||
if (flagBgRenderer != null)
|
||
flagBgRenderer.color = playerInfo.Color;
|
||
}
|
||
var t = VFXObject.transform.Find("FlagIcon");
|
||
var flagIconRenderer = t != null ? t.GetComponent<SpriteRenderer>() : null;
|
||
if (flagIconRenderer == null) return;
|
||
flagIconRenderer.sprite = Sprite;
|
||
Play(playType);
|
||
}
|
||
|
||
public override void UpdateSpecialParam(GridVFXParams param)
|
||
{
|
||
CivId = param.CivId;
|
||
}
|
||
}
|
||
|
||
public class DamageGridVFXRenderer : GridVFXRenderer
|
||
{
|
||
public int Damage;
|
||
|
||
public DamageGridVFXRenderer(GridVFXParams param) : base(param)
|
||
{
|
||
Damage = param.Damage;
|
||
}
|
||
|
||
public DamageGridVFXRenderer(GridVFXParams param, GridVFXInfoDataAssets.GridVFXInfo config) : base(param)
|
||
{
|
||
Damage = param.Damage;
|
||
_config = config;
|
||
}
|
||
|
||
public override void SetPlay(GridVFXPlayType playType = GridVFXPlayType.PlayOnce)
|
||
{
|
||
if (IsPlaying) return;
|
||
if (VFXObject == null) return;
|
||
var t = VFXObject.GetComponent<TextMeshPro>();
|
||
if (t == null) return;
|
||
t.text = Damage.ToString();
|
||
Play(playType);
|
||
|
||
}
|
||
|
||
public override void UpdateSpecialParam(GridVFXParams param)
|
||
{
|
||
Damage = param.Damage;
|
||
}
|
||
|
||
public override void SetIntParam(int param)
|
||
{
|
||
Damage = param;
|
||
}
|
||
}
|
||
|
||
public class ROYALFLAMESGridVFXRenderer : GridVFXRenderer
|
||
{
|
||
public int Damage;
|
||
|
||
public ROYALFLAMESGridVFXRenderer(GridVFXParams param) : base(param)
|
||
{
|
||
}
|
||
|
||
public ROYALFLAMESGridVFXRenderer(GridVFXParams param, GridVFXInfoDataAssets.GridVFXInfo config) : base(param)
|
||
{
|
||
_config = config;
|
||
}
|
||
|
||
public override void SetPlay(GridVFXPlayType playType = GridVFXPlayType.PlayOnce)
|
||
{
|
||
if (IsPlaying) return;
|
||
if (VFXObject == null) return;
|
||
if (Sprite != null && _renderer != null)
|
||
_renderer.sprite = Sprite;
|
||
// 视觉参数改为从配置读取
|
||
ApplyVisualConfig();
|
||
Play(playType);
|
||
}
|
||
}
|
||
public class FogGridVFXRenderer : GridVFXRenderer
|
||
{
|
||
public FogGridVFXRenderer(GridVFXParams param):base(param)
|
||
{
|
||
|
||
}
|
||
}
|
||
|
||
public class TewiFrenchBuffGridVFXRenderer : GridVFXRenderer
|
||
{
|
||
public TewiFrenchBuffGridVFXRenderer(GridVFXParams param) : base(param)
|
||
{
|
||
}
|
||
|
||
public TewiFrenchBuffGridVFXRenderer(GridVFXParams param, GridVFXInfoDataAssets.GridVFXInfo config) : base(param)
|
||
{
|
||
_config = config;
|
||
}
|
||
|
||
public override void SetPlay(GridVFXPlayType playType = GridVFXPlayType.PlayOnce)
|
||
{
|
||
if (IsPlaying) return;
|
||
if (VFXObject == null) return;
|
||
|
||
if (Sprite != null && _renderer != null)
|
||
_renderer.sprite = Sprite;
|
||
ApplyVisualConfig(); // _config 为 null 时会忽略
|
||
Play(playType);
|
||
}
|
||
}
|
||
|
||
public class CoinGridVFXRenderer : GridVFXRenderer
|
||
{
|
||
public CoinGridVFXRenderer(GridVFXParams param) : base(param)
|
||
{
|
||
}
|
||
|
||
public CoinGridVFXRenderer(GridVFXParams param, GridVFXInfoDataAssets.GridVFXInfo config) : base(param)
|
||
{
|
||
_config = config;
|
||
//Debug.Log($"[GridVFX][Coin] 🪙 CTOR VFXObject={(VFXObject != null ? VFXObject.name : "NULL")}, VFXAnim={(VFXAnim != null ? VFXAnim.name : "NULL")}, Sprite={(Sprite != null ? Sprite.name : "NULL")}, _renderer={(_renderer != null ? "OK" : "NULL")}, config.GameObjectPath={(config != null ? config.GameObjectPath : "NULL-CONFIG")}");
|
||
}
|
||
|
||
public override void SetPlay(GridVFXPlayType playType = GridVFXPlayType.PlayOnce)
|
||
{
|
||
//Debug.Log($"[GridVFX][Coin][SetPlay] 🪙 ENTER playType={playType}, IsPlaying={IsPlaying}, VFXObject={(VFXObject != null ? VFXObject.name : "NULL")}, Sprite={(Sprite != null ? Sprite.name : "NULL")}");
|
||
if (IsPlaying)
|
||
{
|
||
//Debug.LogWarning("[GridVFX][Coin][SetPlay] ⚠️ SKIP (already playing)");
|
||
return;
|
||
}
|
||
if (VFXObject == null)
|
||
{
|
||
//Debug.LogWarning("[GridVFX][Coin][SetPlay] ❌ VFXObject NULL — check GridVFXInfo.GameObjectPath for Coin");
|
||
return;
|
||
}
|
||
|
||
if (Sprite != null && _renderer != null)
|
||
_renderer.sprite = Sprite;
|
||
ApplyVisualConfig(); // _config 为 null 时会忽略
|
||
Play(playType);
|
||
}
|
||
}
|
||
|
||
public class SkillIconGridVFXRenderer : GridVFXRenderer
|
||
{
|
||
public SkillType SkillType;
|
||
public UnitFullType UnitFullType;
|
||
private SpriteRenderer _skillBGRenderer;
|
||
|
||
public SkillIconGridVFXRenderer(GridVFXParams param) : base(param)
|
||
{
|
||
SkillType = param.SkillType;
|
||
UnitFullType = param.UnitFullType;
|
||
CacheSkillBGRenderer();
|
||
}
|
||
|
||
public SkillIconGridVFXRenderer(GridVFXParams param, GridVFXInfoDataAssets.GridVFXInfo config) : base(param)
|
||
{
|
||
SkillType = param.SkillType;
|
||
UnitFullType = param.UnitFullType;
|
||
_config = config;
|
||
CacheSkillBGRenderer();
|
||
}
|
||
|
||
private void CacheSkillBGRenderer()
|
||
{
|
||
if (VFXObject == null) return;
|
||
var bgTransform = VFXObject.transform.Find("SkillBG");
|
||
if (bgTransform != null)
|
||
_skillBGRenderer = bgTransform.GetComponent<SpriteRenderer>();
|
||
}
|
||
|
||
public override void UpdateSpecialParam(GridVFXParams param)
|
||
{
|
||
SkillType = param.SkillType;
|
||
UnitFullType = param.UnitFullType;
|
||
}
|
||
|
||
public override void SetPlay(GridVFXPlayType playType = GridVFXPlayType.PlayOnce)
|
||
{
|
||
if (IsPlaying) return;
|
||
if (VFXObject == null) return;
|
||
if (Table.Instance?.SkillDataAssets == null) return;
|
||
if (!Table.Instance.SkillDataAssets.GetSkillIcon(SkillType, UnitFullType, out var skillIcon)) return;
|
||
|
||
if (_renderer == null) return;
|
||
_renderer.sprite = skillIcon;
|
||
|
||
// 根据 SkillViewType 对 SkillBG 染色(对齐 UIInfoCommonBaseSkillCircleMono.SetContent)
|
||
if (_skillBGRenderer != null &&
|
||
Table.Instance.SkillDataAssets.GetSkillInfo(SkillType, out var skillInfo))
|
||
{
|
||
_skillBGRenderer.color = Table.Instance.SkillDataAssets.GetBGColor(skillInfo.SkillViewType, false);
|
||
}
|
||
|
||
ApplyVisualConfig();
|
||
Play(playType);
|
||
}
|
||
}
|
||
|
||
public class FireGridVFXRenderer : GridVFXRenderer
|
||
{
|
||
public FireGridVFXRenderer(GridVFXParams param):base(param) { }
|
||
public override void Play(GridVFXPlayType playType = GridVFXPlayType.PlayOnce)
|
||
{
|
||
IsPlaying = true;
|
||
VFXObject?.SetActive(true);
|
||
}
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// 通用 VFX Renderer - 使用 GridVFXInfoDataAssets 配置
|
||
/// </summary>
|
||
public class GenericGridVFXRenderer : GridVFXRenderer
|
||
{
|
||
public GenericGridVFXRenderer(GridVFXParams param, GridVFXInfoDataAssets.GridVFXInfo config) : base(param)
|
||
{
|
||
_config = config;
|
||
}
|
||
|
||
public override void SetPlay(GridVFXPlayType playType = GridVFXPlayType.PlayOnce)
|
||
{
|
||
if (IsPlaying) return;
|
||
if (VFXObject == null) return;
|
||
|
||
if (Sprite != null && _renderer != null)
|
||
_renderer.sprite = Sprite;
|
||
|
||
//应用配置中的视觉参数
|
||
ApplyVisualConfig();
|
||
|
||
Play(playType);
|
||
}
|
||
|
||
protected override void ApplyVisualConfig()
|
||
{
|
||
if (_config == null || VFXObject == null) return;
|
||
|
||
if (_renderer != null)
|
||
{
|
||
_renderer.color = _config.TintColor;
|
||
}
|
||
|
||
if (_transfrom != null)
|
||
{
|
||
_transfrom.localScale = _config.Scale;
|
||
}
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// Dancing VFX:从中点出发,沿 X 轴 中→左→右→左→右→中 来回跳舞(2 个完整正弦周期,总时长 1.6s)。
|
||
/// 跨过中点向右移动时贴图镜像翻转,向左移动时恢复正常方向。
|
||
/// 不使用 AnimationClip,由 Update 程序化驱动。
|
||
/// </summary>
|
||
public class DancingGridVFXRenderer : GridVFXRenderer
|
||
{
|
||
// 配置常量(如需可调整或挪到 GridVFXInfo 扩展字段)
|
||
private const float Duration = 2.4f; // 总时长:中→左→右→左→右→中 = 2 个完整周期
|
||
private const float Frequency = 0.8333f; // 2.4s 内 2 个完整周期 = 2 / 2.4
|
||
private const float Amplitude = 2.4f; // 位移幅度(世界单位)
|
||
|
||
private Vector3 _centerPos; // 起始 localPosition(视为中点)
|
||
private Vector3 _baseScale; // 起始 localScale(保留正负号)
|
||
private float _absScaleX; // |baseScale.x|,用于翻转
|
||
private float _startTime;
|
||
private bool _wasMirrored;
|
||
|
||
public DancingGridVFXRenderer(GridVFXParams param, GridVFXInfoDataAssets.GridVFXInfo config) : base(param)
|
||
{
|
||
_config = config;
|
||
}
|
||
|
||
public override void SetPlay(GridVFXPlayType playType = GridVFXPlayType.PlayOnce)
|
||
{
|
||
if (IsPlaying) return;
|
||
if (VFXObject == null) return;
|
||
if (Sprite != null && _renderer != null) _renderer.sprite = Sprite;
|
||
ApplyVisualConfig();
|
||
|
||
// 记录起始变换作为中点基准
|
||
_centerPos = _transfrom.localPosition;
|
||
_baseScale = _transfrom.localScale;
|
||
_absScaleX = Mathf.Abs(_baseScale.x);
|
||
_wasMirrored = false;
|
||
_startTime = Time.time;
|
||
|
||
VFXObject.SetActive(true);
|
||
IsPlaying = true;
|
||
|
||
// 到时回收:复位变换后隐藏
|
||
Timer.Instance.TimerRegister(VFXObject, () =>
|
||
{
|
||
if (_transfrom != null)
|
||
{
|
||
_transfrom.localPosition = _centerPos;
|
||
_transfrom.localScale = _baseScale;
|
||
}
|
||
VFXObject?.SetActive(false);
|
||
IsPlaying = false;
|
||
}, Duration, "GridVFXRenderer_Dancing");
|
||
}
|
||
|
||
public override void Update()
|
||
{
|
||
if (!IsPlaying || _transfrom == null) return;
|
||
float t = Time.time - _startTime;
|
||
if (t > Duration) return;
|
||
|
||
// -sin 让第一个半周期往左走,符合"中→左→右→左→右→中"的顺序
|
||
float dx = -Mathf.Sin(2f * Mathf.PI * Frequency * t) * Amplitude;
|
||
_transfrom.localPosition = _centerPos + new Vector3(dx, 0f, 0f);
|
||
|
||
// 在中点右侧时镜像(scale.x 取负),左侧或正中时恢复
|
||
bool shouldMirror = dx > 0f;
|
||
if (shouldMirror != _wasMirrored)
|
||
{
|
||
var s = _transfrom.localScale;
|
||
s.x = shouldMirror ? -_absScaleX : _absScaleX;
|
||
_transfrom.localScale = s;
|
||
_wasMirrored = shouldMirror;
|
||
}
|
||
}
|
||
|
||
public override void Stop()
|
||
{
|
||
IsPlaying = false;
|
||
if (_transfrom != null)
|
||
{
|
||
_transfrom.localPosition = _centerPos;
|
||
_transfrom.localScale = _baseScale;
|
||
}
|
||
VFXObject?.SetActive(false);
|
||
}
|
||
}
|
||
|
||
}
|