diff --git a/Assets/PerfectWorld/Scripts/Managers/A3DSkillGfxMan.cs b/Assets/PerfectWorld/Scripts/Managers/A3DSkillGfxMan.cs
index 9c9322d62f..6611d201e1 100644
--- a/Assets/PerfectWorld/Scripts/Managers/A3DSkillGfxMan.cs
+++ b/Assets/PerfectWorld/Scripts/Managers/A3DSkillGfxMan.cs
@@ -147,11 +147,11 @@ namespace BrewMonster
pEvent.SetTargetID(nTargetID);
pEvent.SetFlyTimeSpan(dwFlyTimeSpan);
pEvent.SetDelay(dwDelayTime);
- //pEvent.SetReverse(bReverse);
- //if (param.value.fVal != float.MinValue) pEvent.SetParam(param);
+ pEvent.SetReverse(bReverse);
+ pEvent.SetParam(param);
pEvent.SetTraceTarget(bTraceTarget);
pEvent.SetModifier(dwModifier);
- //pEvent.SetIsCluster(bCluster);
+ pEvent.SetIsCluster(bCluster);
pEvent.SetFadeOut(bFadeOut);
pEvent.SetGoblinSkill(bIsGoblinSkill);
@@ -190,7 +190,6 @@ namespace BrewMonster
#if !_SKILLGFXCOMPOSER
pEvent.Tick(0);
#endif
- pComposer.SpawnGFX(nTargetID);
PushEvent(pEvent);
return true;
}
@@ -202,7 +201,7 @@ namespace BrewMonster
public class A3DSkillGfxEvent
{
protected A3DSkillGfxComposer m_pComposer;
- //protected CGfxMoveBase m_pMoveMethod;
+ protected CGfxMoveBase m_pMoveMethod;
//protected A3DGFXEx m_pFlyGfx; // 飞行特效 / Fly effect
//protected A3DGFXEx m_pHitGfx; // 命中特效 / Hit effect
protected uint m_dwFlyTimeSpan; // 飞行时间 / Flight time
@@ -248,7 +247,7 @@ namespace BrewMonster
m_bGfxDisableCamShake = false;
m_bHostECMCreatedByGfx = false;
- //m_pMoveMethod = CGfxMoveBase.CreateMoveMethod(mode);
+ m_pMoveMethod = CGfxMoveBase.CreateMoveMethod(mode);
}
~A3DSkillGfxEvent()
@@ -333,16 +332,16 @@ namespace BrewMonster
public void SetHostModelCreatedByGfx(bool b) { m_bHostECMCreatedByGfx = b; }
public bool GetHostModelCreatedByGfx() { return m_bHostECMCreatedByGfx; }
public void SetComposer(A3DSkillGfxComposer pComposer) { m_pComposer = pComposer; }
- //public CGfxMoveBase GetMoveMethod() { return m_pMoveMethod; }
- //public GfxMoveMode GetMode() { return m_pMoveMethod.GetMode(); }
- //public GfxHitPos GetHitPos() { return m_pMoveMethod.GetHitPos(); }
+ public CGfxMoveBase GetMoveMethod() { return m_pMoveMethod; }
+ public GfxMoveMode GetMode() { return m_pMoveMethod.GetMode(); }
+ public GfxHitPos GetHitPos() { return m_pMoveMethod.GetHitPos(); }
/* public A3DGFXEx GetFlyGfx() { return m_pFlyGfx; }
public A3DGFXEx GetHitGfx() { return m_pHitGfx; }*/
public void SetFlyTimeSpan(uint dwSpan) { m_dwFlyTimeSpan = dwSpan; }
public void SetDelay(uint dwDelay) { m_dwDelayTime = dwDelay; }
- //public void SetReverse(bool bReverse) { m_pMoveMethod.SetReverse(bReverse); }
- //public void SetParam(GFX_SKILL_PARAM param) { m_pMoveMethod.SetParam(param); }
- //public void SetIsCluster(bool bCluster) { m_pMoveMethod.SetIsCluster(bCluster); }
+ public void SetReverse(bool bReverse) { m_pMoveMethod.SetReverse(bReverse); }
+ public void SetParam(GFX_SKILL_PARAM param) { m_pMoveMethod.SetParam(param); }
+ public void SetIsCluster(bool bCluster) { m_pMoveMethod.SetIsCluster(bCluster); }
public void SetTraceTarget(bool bTrace) { m_bTraceTarget = bTrace; }
public void SetFadeOut(bool bFadeOut) { m_bFadeOut = bFadeOut; }
public bool IsFinished() { return m_enumState == GfxSkillEventState.enumFinished; }
@@ -393,32 +392,17 @@ namespace BrewMonster
if (m_enumState == GfxSkillEventState.enumFinished) return; // 结束 / Finished
else if (m_enumState == GfxSkillEventState.enumHit) // 命中 / Hit
{
- /* if (m_pHitGfx == null || m_pHitGfx.GetState() == ST_STOP)
- m_enumState = GfxSkillEventState.enumFinished;
- else
- {
- if (!m_bTargetExist || (m_bHitGfxInfinite && m_pHitGfx.GetTimeElapse() > 5000)) // HIT_GFX_MAX_TIMESPAN = 5000
- m_enumState = GfxSkillEventState.enumFinished;
- else
- {
- if (m_bTraceTarget)
- {
- Matrix4x4 matTran = Matrix4x4.identity;
- Vector3 targetCenter = GetTargetCenter();
- matTran.SetColumn(3, new Vector4(targetCenter.x, targetCenter.y, targetCenter.z, 1));
- //m_pHitGfx.SetParentTM(matTran);
- }
-
- //m_pHitGfx.TickAnimation(dwDeltaTime);
- }
- }*/
+ // In Unity, hit GFX is auto-destroyed via Destroy(obj, 3f) in CECSkillGfxEvent.
+ // Transition to Finished immediately — the hit GFX cleanup is handled by Unity's timer.
+ // 在Unity中,命中特效通过Destroy(obj, 3f)自动销毁。立即转为Finished状态。
+ m_enumState = GfxSkillEventState.enumFinished;
}
else if (m_dwCurSpan > m_dwFlyTimeSpan) // 飞行超时 / Flight timeout
{
if (!m_bTargetExist)
m_enumState = GfxSkillEventState.enumFinished;
- /* else
- HitTarget(GetTargetCenter());*/
+ else
+ HitTarget(GetTargetCenter());
}
else if (!m_bTargetExist)
m_enumState = GfxSkillEventState.enumFinished;
@@ -431,40 +415,20 @@ namespace BrewMonster
else
{
m_enumState = GfxSkillEventState.enumFlying;
- /* m_pMoveMethod.SetMaxFlyTime(m_dwFlyTimeSpan);
- m_pMoveMethod.StartMove(m_vHostPos, m_vTargetPos);*/
+ m_pMoveMethod.SetMaxFlyTime(m_dwFlyTimeSpan);
+ m_pMoveMethod.StartMove(m_vHostPos, m_vTargetPos);
- /* if (m_pFlyGfx != null)
- {
- Vector3 vDir, vUp;
-
- if (m_pMoveMethod.GetMode() == GfxMoveMode.enumOnTarget && m_pMoveMethod.IsReverse() && GetTargetDirAndUp(out vDir, out vUp))
- m_pFlyGfx.SetParentTM(a3d_TransformMatrix(vDir, vUp, m_pMoveMethod.GetPos()));
- else
- m_pFlyGfx.SetParentTM(_build_matrix(m_pMoveMethod.GetMoveDir(), m_pMoveMethod.GetPos()));
-
- m_pFlyGfx.Start(true);
- m_pMoveMethod.UpdateGfxParam(m_pFlyGfx, m_vHostPos, m_vTargetPos);
- m_pFlyGfx.TickAnimation(0);
- }*/
+ // Fly GFX spawning is handled by CECSkillGfxEvent.Tick() when it detects Wait→Flying transition
+ // 飞行特效的生成由CECSkillGfxEvent.Tick()在检测到Wait→Flying转换时处理
}
}
- else
+ else // enumFlying state / 飞行状态
{
- /* if (m_pMoveMethod.TickMove(dwDeltaTime, m_vHostPos, m_vTargetPos)) // 目标被命中 / Target hit
- HitTarget(GetTargetCenter());*/
- /*else if (m_pFlyGfx != null)
- {
- Vector3 vDir, vUp;
+ if (m_pMoveMethod.TickMove(dwDeltaTime, m_vHostPos, m_vTargetPos)) // 目标被命中 / Target hit
+ HitTarget(GetTargetCenter());
- if (m_pMoveMethod.GetMode() == GfxMoveMode.enumOnTarget && m_pMoveMethod.IsReverse() && GetTargetDirAndUp(out vDir, out vUp))
- m_pFlyGfx.SetParentTM(a3d_TransformMatrix(vDir, vUp, m_pMoveMethod.GetPos()));
- else
- m_pFlyGfx.SetParentTM(_build_matrix(m_pMoveMethod.GetMoveDir(), m_pMoveMethod.GetPos()));
-
- m_pMoveMethod.UpdateGfxParam(m_pFlyGfx, m_vHostPos, m_vTargetPos);
- m_pFlyGfx.TickAnimation(dwDeltaTime);
- }*/
+ // Fly GFX transform update is handled by CECSkillGfxEvent.Tick()
+ // 飞行特效的变换更新由CECSkillGfxEvent.Tick()处理
}
}
diff --git a/Assets/PerfectWorld/Scripts/Managers/CECAttacksMan.cs b/Assets/PerfectWorld/Scripts/Managers/CECAttacksMan.cs
index 31aba54c2f..bf07c3e77c 100644
--- a/Assets/PerfectWorld/Scripts/Managers/CECAttacksMan.cs
+++ b/Assets/PerfectWorld/Scripts/Managers/CECAttacksMan.cs
@@ -141,6 +141,8 @@ namespace BrewMonster
private void Update()
{
+ uint dwDeltaTime = (uint)(Time.deltaTime * 1000);
+
#if UNITY_EDITOR
if (m_AttackList.Count == 0)
m_AttackList = m_targets.ToList();
@@ -152,9 +154,13 @@ namespace BrewMonster
//BMLogger.LogError("HoangDev: Update CECAttackEvent node.Value.m_bFinished: " + node.Value.m_bFinished);
if (node.Value.m_bFinished)
m_targets.Remove(node);
- else node.Value.Tick((uint)(Time.deltaTime * 1000));
+ else node.Value.Tick(dwDeltaTime);
node = next;
}
+
+ // Tick skill GFX events (fly/hit GFX state machine)
+ // 更新技能特效事件(飞行/命中特效状态机)
+ SkillGfxMan.InstanceSub.Tick(dwDeltaTime);
}
bool FileExists(string relativePath)
{
@@ -454,6 +460,36 @@ namespace BrewMonster
};
}
+ ///
+ /// Load SkillStub GFX parameters onto this composer.
+ /// 将SkillStub的GFX参数加载到此组合器上。
+ ///
+ public void LoadFromSkillStub(BrewMonster.Scripts.Skills.SkillStub stub)
+ {
+ if (stub == null) return;
+ m_MoveMode = stub.m_MoveMode;
+ m_TargetMode = stub.m_TargetMode;
+ m_AttFlyMode = stub.m_AttFlyMode;
+ m_AttHitMode = stub.m_AttHitMode;
+ m_dwFlyTime = stub.m_dwFlyTime;
+ m_bTraceTarget = stub.m_bTraceTarget;
+ m_FlyCluster = new GfxCluster
+ {
+ m_ulCount = stub.m_FlyClusterCount,
+ m_dwInterv = stub.m_FlyClusterInterval
+ };
+ m_bOneHit = stub.m_bOneHit;
+ m_bFadeOut = stub.m_bFadeOut;
+ m_bRelScl = stub.m_bRelScl;
+ m_fDefTarScl = stub.m_fDefTarScl;
+ m_param = stub.m_param;
+ }
+
+ // GFX prefab accessors / GFX预制体访问器
+ public GameObject GetFlyGFX() => flyGFX;
+ public GameObject GetHitGFX() => hitGFX;
+ public GameObject GetHitGrdGFX() => hitGrdGFX;
+
///
/// Load composer from file
/// 从文件加载组合器
@@ -465,11 +501,6 @@ namespace BrewMonster
#endif
public async Task Load(SkillStub skillStub, string flyGFXPath, string hitGrdGFXPath, string hitGFXPath)
{
-#if !UNITY_EDITOR
- string flyGfxName = flyGFXPath;
- string hitGfxName = hitGFXPath;
- string hitGrdGfxName = hitGrdGFXPath;
-#else
flyGfxName = flyGFXPath;
hitGfxName = hitGFXPath;
hitGrdGfxName = hitGrdGFXPath;
@@ -537,19 +568,8 @@ namespace BrewMonster
return true;
}
- public void SpawnGFX(long IDTarget)
- {
-#if UNITY_EDITOR
- BMLogger.LogError("HoangDev: Load A3DSkillGfxComposer GFX name: " + flyGfxName);
- BMLogger.LogError("HoangDev: Load A3DSkillGfxComposer GFX name: " + hitGfxName);
- BMLogger.LogError("HoangDev: Load A3DSkillGfxComposer GFX name: " + hitGrdGfxName);
-#endif
- var obj = EC_ManMessageMono.Instance.GetObject(IDTarget, 0);
- if (obj != null && flyGFX != null)
- {
- GameObject.Instantiate(flyGFX, obj.transform.position, flyGFX.gameObject.transform.rotation, obj.transform);
- }
- }
+ // SpawnGFX temp hack REMOVED — GFX spawning now handled by CECSkillGfxEvent state machine
+ // SpawnGFX临时代码已删除 — GFX生成现在由CECSkillGfxEvent状态机处理
///
/// Initialize composer
/// 初始化组合器
@@ -567,45 +587,33 @@ namespace BrewMonster
{
bool bCastInTargets = false;
- /* char szFly = m_szFlyGfx[0] ? m_szFlyGfx : NULL;
- char szHit = m_szHitGfx[0] ? m_szHitGfx : NULL;*/
+ // Determine GFX names from loaded prefabs / 从已加载的预制体确定GFX名称
+ string szFly = flyGFX != null ? flyGfxName : null;
+ string szHit = hitGFX != null ? hitGfxName : null;
- /*# ifndef _SKILLGFXCOMPOSER
- if (!CECOptimize::Instance().GetGFX().CanShowFly(nHostID))
- {
- a_LogOutput(1, "[SKILL_GFX_FLOW] ====> Fly GFX hidden by optimization");
- szFly = NULL;
- }
- if (!CECOptimize::Instance().GetGFX().CanShowHit(nHostID))
- {
- a_LogOutput(1, "[SKILL_GFX_FLOW] ====> Hit GFX hidden by optimization");
- szHit = NULL;
- }
- #endif*/
+ // TODO Phase 2: Optimization checks / 第二阶段:优化检查
+ // if (!CECOptimize.Instance.GetGFX().CanShowFly(nHostID)) szFly = null;
+ // if (!CECOptimize.Instance.GetGFX().CanShowHit(nHostID)) szHit = null;
- // Log target details
if (targets != null && targets.Count > 0)
{
for (int i = 0; i < targets.Count; i++)
{
var tar = targets[i];
-
if (nCastTargetID == tar.idTarget)
bCastInTargets = true;
- AddOneTarget(nCastTargetID, nHostID, "" /*szFly*/, ""/*szHit*/, tar, i == 0, bIsGoblinSkill);
+ AddOneTarget(nCastTargetID, nHostID, szFly, szHit, tar, i == 0, bIsGoblinSkill);
}
}
- else
- {
- }
+
if (nCastTargetID != 0 && !bCastInTargets)
{
TARGET_DATA tar = default;
tar.idTarget = nCastTargetID;
tar.dwModifier = 0;
- AddOneTarget(nCastTargetID, nHostID, ""/*szFly*/, ""/*szHit*/, tar, false, bIsGoblinSkill);
+ AddOneTarget(nCastTargetID, nHostID, szFly, szHit, tar, false, bIsGoblinSkill);
}
}
@@ -642,10 +650,10 @@ namespace BrewMonster
}
// 计算缩放 / Calculate scale
- /* if (m_bRelScl)
- fScale = m_pSkillGfxMan.GetTargetScale(_Target) / m_fDefTarScl * m_fHitGfxScale;
- else
- fScale = m_fHitGfxScale;*/
+ if (m_bRelScl)
+ fScale = SkillGfxMan.InstanceSub.GetTargetScale(_Target) / m_fDefTarScl * m_fHitGfxScale;
+ else
+ fScale = m_fHitGfxScale;
// 根据目标类型决定是否显示特效 / Determine whether to show effects based on target type
if ((nCastTargetID != 0 && tar.idTarget != nCastTargetID)
@@ -675,8 +683,8 @@ namespace BrewMonster
(int)m_FlyCluster.m_ulCount,
m_FlyCluster.m_dwInterv,
m_param,
- 0/*m_fFlyGfxScale*/,
- 0/*fScale*/,
+ m_fFlyGfxScale,
+ fScale,
tar.dwModifier,
m_bOneHit,
m_bFadeOut,
@@ -981,9 +989,13 @@ public class CECAttackEvent
}
else
{
- // TODO: Implement SkillGfxComposerMan
- // m_pManager.GetSkillGfxComposerMan().Play(m_idSkill, m_idHost, m_idCastTarget, m_targets);
- // pComposer = m_pManager.GetSkillGfxComposerMan().GetSkillGfxComposer(m_idSkill);
+ // NPC regular skill GFX / NPC常规技能特效
+ var composerMan = m_pManager?.GetSkillGfxComposerMan();
+ if (composerMan != null)
+ {
+ composerMan.Play(m_idSkill, m_idHost, m_idCastTarget, m_targets);
+ pComposer = composerMan.GetSkillGfxComposer(m_idSkill);
+ }
}
if (pComposer != null && pComposer.m_dwFlyTime == 0) // 技能没有飞行时间,则直接头顶冒字 / Skill has no fly time, show damage immediately
diff --git a/Assets/PerfectWorld/Scripts/Managers/CECSkillGfxMan.cs b/Assets/PerfectWorld/Scripts/Managers/CECSkillGfxMan.cs
index 706e00bf3c..37db22d9c6 100644
--- a/Assets/PerfectWorld/Scripts/Managers/CECSkillGfxMan.cs
+++ b/Assets/PerfectWorld/Scripts/Managers/CECSkillGfxMan.cs
@@ -15,6 +15,9 @@ namespace BrewMonster
protected EC_ManPlayer m_pPlayerMan; // 玩家管理器 / Player manager
protected CECNPCMan m_pNPCMan; // NPC管理器 / NPC manager
+ private GameObject m_flyGfxInstance; // 飞行特效实例 / Fly GFX instance
+ private GameObject m_hitGfxInstance; // 命中特效实例 / Hit GFX instance
+
public CECSkillGfxEvent(GfxMoveMode mode) : base(mode)
{
m_pPlayerMan = EC_ManMessageMono.Instance?.GetECManPlayer;
@@ -34,7 +37,7 @@ namespace BrewMonster
/// Get the original host ID (considering reverse mode)
/// 获取原始施法者ID(考虑反向模式)
///
- /* public long GetOriginalHost()
+ public long GetOriginalHost()
{
// GFX 特效显示,技能的原始的攻击者和目标被位置,提供了方法,查询原始攻击者
// GFX effects display, original attacker and target positions swapped, provides method to query original attacker
@@ -50,7 +53,7 @@ namespace BrewMonster
// GFX 特效显示,技能的原始的攻击者和目标被位置,提供了方法,查询原始目标
// GFX effects display, original attacker and target positions swapped, provides method to query original target
return m_pMoveMethod.IsReverse() ? m_nHostID : m_nTargetID;
- }*/
+ }
///
/// Get target direction and up vector
@@ -122,7 +125,8 @@ namespace BrewMonster
///
public override void Tick(uint dwDeltaTime)
{
- /*if (GetComposer() != null)
+ // Update host and target positions / 更新施法者和目标位置
+ if (GetComposer() != null)
{
SGC_POS_INFO pHostPos, pTargetPos;
@@ -192,51 +196,115 @@ namespace BrewMonster
Vector3.zero,
null,
false);
- }*/
+ }
+ // Track state before base.Tick() to detect transitions / 在base.Tick()前记录状态以检测转换
+ GfxSkillEventState prevState = m_enumState;
base.Tick(dwDeltaTime);
+
+ // Spawn fly GFX when entering Flying state / 进入飞行状态时生成飞行特效
+ if (prevState == GfxSkillEventState.enumWait && m_enumState == GfxSkillEventState.enumFlying)
+ SpawnFlyGfx();
+
+ // Update fly GFX transform during Flying / 飞行期间更新飞行特效变换
+ if (m_enumState == GfxSkillEventState.enumFlying)
+ UpdateFlyGfxTransform();
}
///
- /// Handle target hit event
- /// 处理命中目标事件
+ /// Handle target hit event - destroy fly GFX and spawn hit GFX
+ /// 处理命中目标事件 - 销毁飞行特效并生成命中特效
///
protected override void HitTarget(Vector3 vTarget)
{
base.HitTarget(vTarget);
+ DestroyFlyGfx();
+ SpawnHitGfx(vTarget);
- // now show some special hit gfx
- // 现在显示一些特殊的命中特效
- /* if (CECOptimize.Instance.GetGFX().CanShowHit(GetOriginalHost()))
+ // TODO Phase 2: Special hit effects (rune, critical, nullity)
+ // TODO 第二阶段:特殊命中效果(符石、暴击、无效)
+ }
+
+ // ===== GFX Instance Management =====
+ // GFX实例管理
+
+ ///
+ /// Spawn fly GFX at movement position
+ /// 在移动位置生成飞行特效
+ ///
+ private void SpawnFlyGfx()
+ {
+ if (m_pComposer == null) return;
+ GameObject prefab = m_pComposer.GetFlyGFX();
+ if (prefab == null) return;
+
+ Vector3 pos = m_pMoveMethod.GetPos();
+ Vector3 dir = m_pMoveMethod.GetMoveDir();
+ Quaternion rot = dir.sqrMagnitude > 1e-4f ? Quaternion.LookRotation(dir) : Quaternion.identity;
+ m_flyGfxInstance = GameObject.Instantiate(prefab, pos, rot);
+ BMLogger.Log($"[GFX_FLOW] SpawnFlyGfx at {pos}, prefab={prefab.name}");
+ }
+
+ ///
+ /// Update fly GFX transform to follow movement
+ /// 更新飞行特效变换以跟随移动
+ ///
+ private void UpdateFlyGfxTransform()
+ {
+ if (m_flyGfxInstance == null) return;
+ m_flyGfxInstance.transform.position = m_pMoveMethod.GetPos();
+ Vector3 dir = m_pMoveMethod.GetMoveDir();
+ if (dir.sqrMagnitude > 1e-4f)
+ m_flyGfxInstance.transform.rotation = Quaternion.LookRotation(dir);
+ }
+
+ ///
+ /// Destroy fly GFX instance
+ /// 销毁飞行特效实例
+ ///
+ private void DestroyFlyGfx()
+ {
+ if (m_flyGfxInstance != null)
{
- if ((m_dwModifier & CECAttackEvent.MOD_PHYSIC_ATTACK_RUNE) != 0)
- {
- // 程序联入\\符石\\物攻符石特效.gfx
- // Program integration\\Rune\\Physical attack rune effect.gfx
- // TODO: Play GFX - g_pGame->GetGFXCaster()->PlayAutoGFXEx(...)
- }
+ GameObject.Destroy(m_flyGfxInstance);
+ m_flyGfxInstance = null;
+ }
+ }
- if ((m_dwModifier & CECAttackEvent.MOD_MAGIC_ATTACK_RUNE) != 0)
- {
- // 程序联入\\符石\\法攻符石特效.gfx
- // Program integration\\Rune\\Magic attack rune effect.gfx
- // TODO: Play GFX
- }
+ ///
+ /// Spawn hit GFX at target position
+ /// 在目标位置生成命中特效
+ ///
+ private void SpawnHitGfx(Vector3 vTarget)
+ {
+ if (m_pComposer == null) return;
+ GameObject prefab = m_pComposer.GetHitGFX();
+ if (prefab == null) return;
- if ((m_dwModifier & CECAttackEvent.MOD_PHYSIC_DEFENCE_RUNE) != 0)
- {
- // 程序联入\\符石\\物防符石特效.gfx
- // Program integration\\Rune\\Physical defense rune effect.gfx
- // TODO: Play GFX
- }
+ Quaternion rot = Quaternion.identity;
+ if (m_bHostExist)
+ {
+ Vector3 dir = vTarget - m_vHostPos;
+ dir.y = 0;
+ if (dir.sqrMagnitude > 1e-6f) rot = Quaternion.LookRotation(dir);
+ }
- if ((m_dwModifier & CECAttackEvent.MOD_MAGIC_DEFENCE_RUNE) != 0)
- {
- // 程序联入\\符石\\法防符石特效.gfx
- // Program integration\\Rune\\Magic defense rune effect.gfx
- // TODO: Play GFX
- }
- }*/
+ m_hitGfxInstance = GameObject.Instantiate(prefab, vTarget, rot);
+ GameObject.Destroy(m_hitGfxInstance, 3.0f); // auto-cleanup / 自动清理
+ BMLogger.Log($"[GFX_FLOW] SpawnHitGfx at {vTarget}, prefab={prefab.name}");
+ }
+
+ ///
+ /// Clean up GFX instances on Resume (return to pool)
+ /// 在Resume时清理GFX实例(返回池)
+ ///
+ public new void Resume()
+ {
+ DestroyFlyGfx();
+ // Hit GFX is auto-destroyed by Unity's Destroy timer, don't null it here
+ // 命中特效由Unity的Destroy计时器自动销毁,不在此处置null
+ m_hitGfxInstance = null;
+ base.Resume();
}
///
@@ -672,7 +740,7 @@ namespace BrewMonster
{
m_EventLst.Remove(node);
pEvent.Resume();
- //m_FreeLst[(int)pEvent.GetMode()].AddLast(pEvent);
+ m_FreeLst[(int)pEvent.GetMode()].AddLast(pEvent);
}
else
{
diff --git a/Assets/PerfectWorld/Scripts/Vfx/A3DSkillGfxComposerMan.cs b/Assets/PerfectWorld/Scripts/Vfx/A3DSkillGfxComposerMan.cs
index 6238acea12..139847efde 100644
--- a/Assets/PerfectWorld/Scripts/Vfx/A3DSkillGfxComposerMan.cs
+++ b/Assets/PerfectWorld/Scripts/Vfx/A3DSkillGfxComposerMan.cs
@@ -50,8 +50,15 @@ namespace BrewMonster
return false;
}
+ // Load SkillStub GFX parameters (MoveMode, FlyTime, TargetMode, clusters, etc.)
+ // 从SkillStub加载GFX参数(移动模式、飞行时间、目标模式、群集等)
+ SkillStub stub = SkillStub.GetStub((uint)nSkillID);
+ if (stub != null)
+ {
+ composer.LoadFromSkillStub(stub);
+ }
+
// In original C++: composer->Init(g_pGame->GetGameRun()->GetWorld()->GetSkillGfxMan()->GetPtr());
- // Not wired in Unity yet; keep placeholder init to allow future hookup.
composer.Init(A3DSkillGfxMan.Instance);
m_ComposerMap[nSkillID] = composer;