备份,准备做双端
This commit is contained in:
@@ -15,6 +15,80 @@ public class GfxController : MonoBehaviour
|
||||
// 复用的属性块:命中特效透明度覆写用,避免每次命中访问 r.materials 克隆材质。
|
||||
private MaterialPropertyBlock _fxPropertyBlock;
|
||||
|
||||
// ---- 特效对象池:按 prefab 归类复用实例,避免每次命中/弹道 Instantiate+Destroy 造成的 GC。----
|
||||
private readonly System.Collections.Generic.Dictionary<GameObject, System.Collections.Generic.Stack<GameObject>> _fxPools
|
||||
= new System.Collections.Generic.Dictionary<GameObject, System.Collections.Generic.Stack<GameObject>>();
|
||||
private Transform _fxPoolRoot;
|
||||
|
||||
/// <summary>从池中取出一个 prefab 实例(无则实例化),并挂上 GfxPooledItem 追踪。</summary>
|
||||
private GameObject AcquireFx(GameObject prefab)
|
||||
{
|
||||
if (prefab == null) return null;
|
||||
|
||||
GameObject go = null;
|
||||
if (_fxPools.TryGetValue(prefab, out var stack))
|
||||
{
|
||||
while (stack.Count > 0)
|
||||
{
|
||||
var candidate = stack.Pop();
|
||||
if (candidate != null) { go = candidate; break; }
|
||||
}
|
||||
}
|
||||
|
||||
GfxPooledItem item;
|
||||
if (go == null)
|
||||
{
|
||||
go = Instantiate(prefab);
|
||||
item = go.GetComponent<GfxPooledItem>();
|
||||
if (item == null) item = go.AddComponent<GfxPooledItem>();
|
||||
item.prefabKey = prefab;
|
||||
item.owner = this;
|
||||
item.CaptureOriginals();
|
||||
}
|
||||
else
|
||||
{
|
||||
item = go.GetComponent<GfxPooledItem>();
|
||||
item.ResetToOriginal();
|
||||
go.SetActive(true);
|
||||
}
|
||||
item.released = false;
|
||||
return go;
|
||||
}
|
||||
|
||||
/// <summary>把特效实例还原并压回对应 prefab 的池中。</summary>
|
||||
public void ReleaseFx(GameObject go)
|
||||
{
|
||||
if (go == null) return;
|
||||
var item = go.GetComponent<GfxPooledItem>();
|
||||
if (item == null) { Destroy(go); return; }
|
||||
if (item.released) return;
|
||||
item.released = true;
|
||||
|
||||
if (_fxPoolRoot == null)
|
||||
{
|
||||
var root = new GameObject("_FxPool");
|
||||
root.transform.SetParent(transform, false);
|
||||
_fxPoolRoot = root.transform;
|
||||
}
|
||||
|
||||
go.SetActive(false);
|
||||
go.transform.SetParent(_fxPoolRoot, false);
|
||||
|
||||
if (!_fxPools.TryGetValue(item.prefabKey, out var stack))
|
||||
{
|
||||
stack = new System.Collections.Generic.Stack<GameObject>();
|
||||
_fxPools[item.prefabKey] = stack;
|
||||
}
|
||||
stack.Push(go);
|
||||
}
|
||||
|
||||
/// <summary>延迟 delay 秒后回收(用于非粒子/动画类特效,替代 Destroy(fx, delay))。</summary>
|
||||
private IEnumerator ReleaseFxDelayed(GameObject go, float delay)
|
||||
{
|
||||
yield return new WaitForSeconds(delay);
|
||||
ReleaseFx(go);
|
||||
}
|
||||
|
||||
[Header("Combatant Objects")]
|
||||
public GameObject enemyMoveObject;
|
||||
public GameObject ally01MoveObject;
|
||||
@@ -287,9 +361,10 @@ public class GfxController : MonoBehaviour
|
||||
// 将缩放应用到挂载点物体(visualParent)本身
|
||||
visualParent.transform.localScale = Vector3.one * fxScale;
|
||||
|
||||
// 作为子物体生成
|
||||
fx = Instantiate(fxPrefab, visualParent.transform);
|
||||
|
||||
// 作为子物体生成(从池取出后挂到挂载点下)
|
||||
fx = AcquireFx(fxPrefab);
|
||||
fx.transform.SetParent(visualParent.transform, false);
|
||||
|
||||
// 确保特效在 UI 层级中显示在最前方
|
||||
fx.transform.SetAsLastSibling();
|
||||
|
||||
@@ -329,11 +404,15 @@ public class GfxController : MonoBehaviour
|
||||
else
|
||||
{
|
||||
Vector3 spawnPos = customWorldPos.HasValue ? customWorldPos.Value : (target.transform.position + offsetVector);
|
||||
fx = Instantiate(fxPrefab, spawnPos, Quaternion.identity);
|
||||
fx = AcquireFx(fxPrefab);
|
||||
fx.transform.SetParent(null, false);
|
||||
fx.transform.position = spawnPos;
|
||||
fx.transform.rotation = Quaternion.identity;
|
||||
fx.transform.localScale = Vector3.one * fxScale;
|
||||
}
|
||||
|
||||
// 针对粒子系统的特殊处理
|
||||
// 针对粒子系统的特殊处理。这里采集一次粒子数组,后面 ApplyTransparencyAndRender
|
||||
// 复用它,避免对同一 fx 再做一次 GetComponentsInChildren<ParticleSystem>() 分配。
|
||||
ParticleSystem[] allParticles = fx.GetComponentsInChildren<ParticleSystem>();
|
||||
foreach (var ps in allParticles)
|
||||
{
|
||||
@@ -342,8 +421,8 @@ public class GfxController : MonoBehaviour
|
||||
main.scalingMode = ParticleSystemScalingMode.Hierarchy;
|
||||
}
|
||||
|
||||
// 应用透明度并处理渲染
|
||||
ApplyTransparencyAndRender(fx, transparency, isEnemy ? enemyFxSortingOrder : allyFxSortingOrder);
|
||||
// 应用透明度并处理渲染(复用上面已采集的粒子数组)
|
||||
ApplyTransparencyAndRender(fx, transparency, isEnemy ? enemyFxSortingOrder : allyFxSortingOrder, allParticles);
|
||||
|
||||
if (allParticles.Length > 0)
|
||||
{
|
||||
@@ -352,18 +431,18 @@ public class GfxController : MonoBehaviour
|
||||
// 确保粒子在生成时立即播放
|
||||
ps.Play(true);
|
||||
|
||||
// 设置停止动作,如果是根节点粒子,设置自动销毁
|
||||
// 设置停止动作:根节点粒子停止时回调,用于回收到对象池(替代 Destroy)
|
||||
if (ps.gameObject == fx)
|
||||
{
|
||||
var m = ps.main;
|
||||
m.stopAction = ParticleSystemStopAction.Destroy;
|
||||
m.stopAction = ParticleSystemStopAction.Callback;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果不是粒子系统(例如动画 Prefab),在固定时间后销毁(默认 2 秒)
|
||||
Destroy(fx, 2.0f);
|
||||
// 如果不是粒子系统(例如动画 Prefab),在固定时间后回收(默认 2 秒)
|
||||
StartCoroutine(ReleaseFxDelayed(fx, 2.0f));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -390,11 +469,11 @@ public class GfxController : MonoBehaviour
|
||||
Transform startTransform = visualSource != null ? visualSource.transform : source.transform;
|
||||
Transform endTransform = visualTarget != null ? visualTarget.transform : target.transform;
|
||||
|
||||
// 在源物体位置实例化
|
||||
GameObject projectile = Instantiate(projectileFX, startTransform.position, Quaternion.identity);
|
||||
|
||||
// --- 核心修正:挂载到 GfxController (即当前脚本所在的 Canvas 层级物体) ---
|
||||
projectile.transform.SetParent(this.transform, true);
|
||||
// 在源物体位置实例化(从池取出)
|
||||
GameObject projectile = AcquireFx(projectileFX);
|
||||
projectile.transform.SetParent(this.transform, false);
|
||||
projectile.transform.position = startTransform.position;
|
||||
projectile.transform.rotation = Quaternion.identity;
|
||||
projectile.transform.localScale = Vector3.one * projectileScale;
|
||||
|
||||
// 应用透明度
|
||||
@@ -475,8 +554,10 @@ public class GfxController : MonoBehaviour
|
||||
Transform startTransform = visualSource != null ? visualSource.transform : source.transform;
|
||||
Transform endTransform = visualTarget != null ? visualTarget.transform : target.transform;
|
||||
|
||||
GameObject projectile = Instantiate(prefab, startTransform.position, Quaternion.identity);
|
||||
projectile.transform.SetParent(this.transform, true);
|
||||
GameObject projectile = AcquireFx(prefab);
|
||||
projectile.transform.SetParent(this.transform, false);
|
||||
projectile.transform.position = startTransform.position;
|
||||
projectile.transform.rotation = Quaternion.identity;
|
||||
projectile.transform.localScale = Vector3.one * allySkillProjectileScale;
|
||||
|
||||
// 应用层级(复用 projectileTransparency)
|
||||
@@ -572,19 +653,19 @@ public class GfxController : MonoBehaviour
|
||||
// 到达终点,触发回调并传回终点坐标
|
||||
onComplete?.Invoke(lastEndPos);
|
||||
|
||||
// 到达后销毁
|
||||
// 到达后回收
|
||||
if (projectile != null)
|
||||
{
|
||||
// 如果有粒子系统,先停止发射让残余拖尾自然消失,或者直接销毁
|
||||
// 如果有粒子系统,先停止发射让残余拖尾自然消失,再延迟回收
|
||||
ParticleSystem[] ps = projectile.GetComponentsInChildren<ParticleSystem>();
|
||||
if (ps.Length > 0)
|
||||
{
|
||||
foreach (var p in ps) p.Stop(true, ParticleSystemStopBehavior.StopEmitting);
|
||||
Destroy(projectile, 1.0f); // 给一点时间让残余粒子消失
|
||||
StartCoroutine(ReleaseFxDelayed(projectile, 1.0f)); // 给一点时间让残余粒子消失
|
||||
}
|
||||
else
|
||||
{
|
||||
Destroy(projectile);
|
||||
ReleaseFx(projectile);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -631,18 +712,18 @@ public class GfxController : MonoBehaviour
|
||||
// 到达终点,触发回调并传回终点坐标
|
||||
onComplete?.Invoke(lastEndPos);
|
||||
|
||||
// 到达后销毁
|
||||
// 到达后回收
|
||||
if (projectile != null)
|
||||
{
|
||||
ParticleSystem[] ps = projectile.GetComponentsInChildren<ParticleSystem>();
|
||||
if (ps.Length > 0)
|
||||
{
|
||||
foreach (var p in ps) p.Stop(true, ParticleSystemStopBehavior.StopEmitting);
|
||||
Destroy(projectile, 1.0f); // 给一点时间让残余粒子消失
|
||||
StartCoroutine(ReleaseFxDelayed(projectile, 1.0f)); // 给一点时间让残余粒子消失
|
||||
}
|
||||
else
|
||||
{
|
||||
Destroy(projectile);
|
||||
ReleaseFx(projectile);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -682,7 +763,7 @@ public class GfxController : MonoBehaviour
|
||||
/// <summary>
|
||||
/// 为特效物体及其子物体中的渲染器应用透明度和层级
|
||||
/// </summary>
|
||||
private void ApplyTransparencyAndRender(GameObject fx, float transparency, int sortingOrder)
|
||||
private void ApplyTransparencyAndRender(GameObject fx, float transparency, int sortingOrder, ParticleSystem[] cachedParticles = null)
|
||||
{
|
||||
// 1. 处理所有普通渲染器 (MeshRenderer, SpriteRenderer 等)
|
||||
Renderer[] renderers = fx.GetComponentsInChildren<Renderer>();
|
||||
@@ -717,8 +798,8 @@ public class GfxController : MonoBehaviour
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 处理粒子系统颜色 (考虑不同模式)
|
||||
ParticleSystem[] particles = fx.GetComponentsInChildren<ParticleSystem>();
|
||||
// 2. 处理粒子系统颜色 (考虑不同模式)。若调用方已采集粒子数组则复用,避免重复分配。
|
||||
ParticleSystem[] particles = cachedParticles ?? fx.GetComponentsInChildren<ParticleSystem>();
|
||||
foreach (var ps in particles)
|
||||
{
|
||||
var main = ps.main;
|
||||
@@ -883,9 +964,11 @@ public class GfxController : MonoBehaviour
|
||||
return false;
|
||||
}
|
||||
|
||||
// 生成弹道
|
||||
GameObject projectile = Instantiate(projectilePrefab, enemyObject.transform.position, Quaternion.identity);
|
||||
projectile.transform.SetParent(enemyObject.transform, true);
|
||||
// 生成弹道(从池取出)
|
||||
GameObject projectile = AcquireFx(projectilePrefab);
|
||||
projectile.transform.SetParent(enemyObject.transform, false);
|
||||
projectile.transform.position = enemyObject.transform.position;
|
||||
projectile.transform.rotation = Quaternion.identity;
|
||||
projectile.transform.localScale = Vector3.one * enemyAttackProjectileScale;
|
||||
|
||||
// 应用层级
|
||||
@@ -934,7 +1017,7 @@ public class GfxController : MonoBehaviour
|
||||
// 停止粒子系统
|
||||
ParticleSystem[] ps = projectile.GetComponentsInChildren<ParticleSystem>();
|
||||
foreach (var p in ps) { var m = p.main; m.loop = false; }
|
||||
Destroy(projectile, 0.5f);
|
||||
StartCoroutine(ReleaseFxDelayed(projectile, 0.5f));
|
||||
}
|
||||
|
||||
// 播放 Hit 特效、闪红,并执行实际扣血
|
||||
@@ -1036,11 +1119,12 @@ public class GfxController : MonoBehaviour
|
||||
if (moveObj == null) return;
|
||||
|
||||
SmoothShake ss = moveObj.GetComponent<SmoothShake>();
|
||||
if (ss == null) ss = moveObj.AddComponent<SmoothShake>();
|
||||
bool freshShaker = false;
|
||||
if (ss == null) { ss = moveObj.AddComponent<SmoothShake>(); freshShaker = true; }
|
||||
|
||||
// 修复:确保 Shaker 实例已初始化,避免 NullReferenceException
|
||||
if (ss.positionShake == null) ss.positionShake = new Shaker();
|
||||
if (ss.rotationShake == null) ss.rotationShake = new Shaker();
|
||||
if (ss.positionShake == null) { ss.positionShake = new Shaker(); freshShaker = true; }
|
||||
if (ss.rotationShake == null) { ss.rotationShake = new Shaker(); freshShaker = true; }
|
||||
|
||||
// 选择震动参数
|
||||
Vector3 amplitude = isEnemy ? hitShakeAmplitude : allyHitShakeAmplitude;
|
||||
@@ -1072,10 +1156,14 @@ public class GfxController : MonoBehaviour
|
||||
if (ss.timeSettings.fadeOutCurve == null || ss.timeSettings.fadeOutCurve.length == 0)
|
||||
ss.timeSettings.fadeOutCurve = AnimationCurve.Linear(0, 1, 1, 0);
|
||||
|
||||
// 初始化内部数组(插件在 AddComponent 时 Awake 已经运行,由于当时字段为空,需要重新同步数组)
|
||||
ss.shakers = new Shaker[] { ss.positionShake, ss.rotationShake };
|
||||
ss.sum = new Vector3[2];
|
||||
|
||||
// 初始化内部数组(插件在 AddComponent 时 Awake 已经运行,由于当时字段为空,需要重新同步数组)。
|
||||
// 仅在 Shaker 实例首次创建/变更时重建这两个数组;之后每次命中复用,避免每击两次数组分配。
|
||||
if (freshShaker || ss.shakers == null || ss.shakers.Length != 2 || ss.sum == null || ss.sum.Length != 2)
|
||||
{
|
||||
ss.shakers = new Shaker[] { ss.positionShake, ss.rotationShake };
|
||||
ss.sum = new Vector3[2];
|
||||
}
|
||||
|
||||
// 启动震动
|
||||
ss.StartShake();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user