---技能特效系统,仅存在于客户端 EffectSystemManager = LuaClass("EffectSystemManager") --存储所有特效实例 EffectSystemManager.ExistedEffectList = {} EffectSystemManager.EffectTable = require('Script.Global.EffectTables') --调试信息开关 EffectSystemManager.OpenDebug = GlobalConfigs.OpenDebug function EffectSystemManager.ctor() end ------------------------------------------------------ [[外部接口]] --------------------------------------------------------------- ---播放粒子特效: 默认生成在CasterActor上,若ActorList不为nil则生成在ActorList中Actor的位置 ---@param InstanceId number 特效实例ID ---@param EffectId number 特效ID(保存在EffectTables.lua文件中) ---@param CasterActor AActor 特效发起Actor ---@param ActorList AActor[] 特效目标Actor列表(可为空) ---@param Duration number 特效持续时间(0则表示AutoDestroy) ---@param SpawnLocationType EEffectSpawnLocationType 特效生成位置(Bottom 脚下, Middle 中间, Top 头顶, Attach 吸附骨骼,) ---@param BoneName string 特效吸附骨骼(仅当SpawnLocationType为Attach时才生效) function EffectSystemManager.PlayEffect(InstanceId, EffectId, CasterActor, ActorList, Duration, SpawnLocationType, BoneName) EffectSystemManager.ApplyEffect(InstanceId, EffectId, CasterActor, ActorList, Duration, SpawnLocationType, BoneName) end ---播放指向型粒子特效: 默认CasterActor为起点, DestActor为终点; 若DestActor为nil, 则Rotation为指定角度 ---@param InstanceId number 特效实例ID ---@param EffectId number 特效ID(保存在EffectTables.lua文件中) ---@param CasterActor AActor 特效起始Actor ---@param DestActor AActor 特效终点Actor ---@param Duration number 特效持续时间(0则表示AutoDestroy) ---@param Rotation FRotator 指定角度(DestActor为空时才生效) function EffectSystemManager.PlayDirectionalEffect(InstanceId, EffectId, CasterActor, DestActor, Duration, Rotation) EffectSystemManager.ApplyDirectionalEffect(InstanceId, EffectId, CasterActor, DestActor, Duration, Rotation) end ---播放粒子特效在世界坐标 ---@param InstanceId number 特效实例ID ---@param EffectId number 特效ID(保存在EffectTables.lua文件中) ---@param CasterActor AActor 特效发起Actor(可为空) ---@param Location FVector 指定位置 ---@param Rotation FRotator 指定旋转 ---@param Duration number 特效持续时间(0则表示AutoDestroy) ---@return UParticleSystemComponent function EffectSystemManager.PlayEffectAtLocation(InstanceId, EffectId, CasterActor, Location, Rotation, Duration) return EffectSystemManager.ApplyEffectAtLocation(InstanceId, EffectId, CasterActor, Location, Rotation, Duration) end ---获取特效实例 ---可在获取后做进一步处理,如调整特效位置 ---根据InstanceId获取EffectInstance ---@param InstanceId number 特效实例ID ---@return EffectInstance function EffectSystemManager.GetEffectInstanceWithId(InstanceId) for _, v in pairs(EffectSystemManager.ExistedEffectList) do if v.InstanceId == InstanceId then return v end end end ---根据CasterActor获取所有关联EffectInstance ---@param CasterActor AActor 特效发起Actor ------@return EffectInstance[] function EffectSystemManager.GetEffectInstanceListOfActor(CasterActor) local EffectInstanceList = {} for _, v in pairs(EffectSystemManager.ExistedEffectList) do if v.CasterActor == CasterActor then table.insert(EffectInstanceList, v) end end return EffectInstanceList end --------------------------------------------------------- [[内部调用]] --------------------------------------------------------- function EffectSystemManager.ApplyEffect(InstanceId, EffectId, CasterActor, ActorList, Duration, SpawnLocationType, BoneName) if InstanceId == nil or EffectId == nil or CasterActor == nil then return end if Duration == nil or type(Duration) ~= "number" then Duration = 0 end if SpawnLocationType == nil then SpawnLocationType = EEffectSpawnLocationType.Bottom end local bAutoDestroy = Duration < 0.01 local bSpawnEmitterSuccess = EffectSystemManager.PlayParticleSystem(InstanceId, EffectId, CasterActor, ActorList, SpawnLocationType, BoneName, bAutoDestroy) if not bSpawnEmitterSuccess then UE.LogError("[EffectSystemManager.ApplyEffect] Failed. EffectId: %d", EffectId) return end if bAutoDestroy == false then CasterActor.EffectDurationTimer = EventSystem.SetTimer(CasterActor, function() EffectSystemManager.RemoveEffectByInstanceId(InstanceId) CasterActor.EffectDurationTimer = nil end, Duration) end UE.Log("[EffectSystemManager.ApplyEffect] Success. InstanceId: %d, EffectId: %d", InstanceId, EffectId) end function EffectSystemManager.ApplyDirectionalEffect(InstanceId, EffectId, CasterActor, DestActor, Duration, Rotation) if InstanceId == nil or EffectId == nil or CasterActor == nil then return end if Duration == nil or type(Duration) ~= "number" then Duration = 0 end local bAutoDestroy = Duration < 0.01 local bSpawnEmitterSuccess = EffectSystemManager.PlayDirectionalParticleSystem(InstanceId, EffectId, CasterActor, DestActor, Rotation, bAutoDestroy) if not bSpawnEmitterSuccess then UE.LogError("[EffectSystemManager.ApplyDirectionalEffect] Failed. EffectId: %d", EffectId) return end if bAutoDestroy == false then CasterActor.EffectDurationTimer = EventSystem.SetTimer(CasterActor, function() EffectSystemManager.RemoveEffectByInstanceId(InstanceId) CasterActor.EffectDurationTimer = nil end, Duration) end UE.Log("[EffectSystemManager.ApplyDirectionalEffect] Success. InstanceId: %d, EffectId: %d", InstanceId, EffectId) end function EffectSystemManager.ApplyEffectAtLocation(InstanceId, EffectId, CasterActor, Location, Rotation, Duration) local ParticleTemplate = EffectSystemManager.GetEffectTemplateById(EffectId) if ParticleTemplate == nil then UE.LogError("[EffectSystemManager.ApplyEffectAtLocation] invalid EffectId: %d", EffectId) return nil end if Duration == nil or type(Duration) ~= "number" then Duration = 0 end local bAutoDestroy = Duration < 0.01 local CurEmitter = GameplayStatics.SpawnEmitterAtLocation(CasterActor, ParticleTemplate, Location, Rotation, VectorHelper.ScaleOne(), bAutoDestroy) if CurEmitter ~= nil and bAutoDestroy == false then CasterActor.EffectDurationTimer = EventSystem.SetTimer(CasterActor, function() EffectSystemManager.RemoveEffectByInstanceId(InstanceId) CasterActor.EffectDurationTimer = nil end, Duration) end UE.Log("[EffectSystemManager.ApplyEffectAtLocation] %s. InstanceId: %d, EffectId: %d", CurEmitter ~= nil and "Success" or "Failed", InstanceId, EffectId) return CurEmitter end function EffectSystemManager.GetEffectTemplateById(EffectId) local EffectTemplate = AsyncLoadTools.GetEffectByIndex(EffectId) if EffectTemplate == nil then local Path = EffectSystemManager.EffectTable.Paths[EffectId] if Path ~= nil then EffectTemplate = UE.LoadObject(Path) end end return EffectTemplate end function EffectSystemManager.UpdateEffectInstanceList(InstanceId, EffectId, EmitterList, CasterActor) local EffectInstance = {} EffectInstance.InstanceId = InstanceId EffectInstance.EffectName = EffectId EffectInstance.EmitterList = EmitterList EffectInstance.CasterActor = CasterActor table.insert(EffectSystemManager.ExistedEffectList, EffectInstance) end function EffectSystemManager.PlayParticleSystem(InstanceId, EffectId, CasterActor, ActorList, SpawnLocationType, BoneName, bAutoDestroy) local ParticleTemplate = EffectSystemManager.GetEffectTemplateById(EffectId) if ParticleTemplate == nil then UE.LogError("[EffectSystemManager.PlayParticleSystem] invalid EffectId: "..tostring(EffectId)) return false end local EmitterList = {} if ActorList == nil then local CurEmitter = nil if SpawnLocationType == EEffectSpawnLocationType.Attach then if BoneName == nil then BoneName = "" end CurEmitter = GameplayStatics.SpawnEmitterAttached(ParticleTemplate, CasterActor.Mesh, BoneName, VectorHelper.VectorZero(), VectorHelper.RotZero(), VectorHelper.ScaleOne(), EAttachLocation.SnapToTarget, bAutoDestroy) else local SpawnRot = CasterActor:K2_GetActorRotation() local SpawnLoc = CasterActor:K2_GetActorLocation() local SpawnLocZ = SpawnLoc.Z if SpawnLocationType == EEffectSpawnLocationType.Bottom then SpawnLocZ = SpawnLocZ - CasterActor.CapsuleComponent.CapsuleHalfHeight elseif SpawnLocationType == EEffectSpawnLocationType.Top then SpawnLocZ = SpawnLocZ + CasterActor.CapsuleComponent.CapsuleHalfHeight end SpawnLoc = {X = SpawnLoc.X, Y = SpawnLoc.Y, Z = SpawnLocZ} CurEmitter = GameplayStatics.SpawnEmitterAtLocation(CasterActor, ParticleTemplate, SpawnLoc, SpawnRot, VectorHelper.ScaleOne(), bAutoDestroy) end if CurEmitter ~= nil then table.insert(EmitterList, CurEmitter) end else for _, Actor in pairs(ActorList) do local CurEmitter = nil if SpawnLocationType == EEffectSpawnLocationType.Attach then if BoneName == nil then BoneName = "" end CurEmitter = GameplayStatics.SpawnEmitterAttached(ParticleTemplate, Actor.Mesh, BoneName, VectorHelper.VectorZero(), VectorHelper.RotZero(), VectorHelper.ScaleOne(), EAttachLocation.SnapToTarget, bAutoDestroy) else local SpawnRot = Actor:K2_GetActorRotation() local SpawnLoc = Actor:K2_GetActorLocation() local SpawnLocZ = SpawnLocationType == EEffectSpawnLocationType.Bottom and SpawnLoc.Z - CasterActor.CapsuleComponent.CapsuleHalfHeight or SpawnLoc.Z + CasterActor.CapsuleComponent.CapsuleHalfHeight SpawnLoc = {X = SpawnLoc.X, Y = SpawnLoc.Y, Z = SpawnLocZ} CurEmitter = GameplayStatics.SpawnEmitterAtLocation(Actor, ParticleTemplate, SpawnLoc, SpawnRot, VectorHelper.ScaleOne(), bAutoDestroy) end if CurEmitter ~= nil then table.insert(EmitterList, CurEmitter) --EffectSystemManager.UpdateEffectInstanceList(InstanceId, EffectId, {CurEmitter}, Actor) end end end EffectSystemManager.UpdateEffectInstanceList(InstanceId, EffectId, EmitterList, CasterActor) return not table.isEmpty(EmitterList) end function EffectSystemManager.PlayDirectionalParticleSystem(InstanceId, EffectId, CasterActor, DestActor, Rotation, bAutoDestroy) local ParticleTemplate = EffectSystemManager.GetEffectTemplateById(EffectId) if ParticleTemplate == nil then UE.LogError("[EffectSystemManager.PlayDirectionalParticleSystem] invalid EffectId: "..tostring(EffectId)) return false end local EmitterList = {} local SpawnLoc = CasterActor:K2_GetActorLocation() local SpawnRot = VectorHelper.RotZero() if DestActor ~= nil then local DestLoc = DestActor:K2_GetActorLocation() SpawnRot = KismetMathLibrary.FindLookAtRotation(SpawnLoc, DestLoc) if GlobalConfigs.OpenDebug then STExtraGameplayStatics.ClientDrawDebugLine(SpawnLoc, DestLoc, {R=1,G=0,B=0,A=1}, 5, 1) end else SpawnRot = Rotation end local CurEmitter = GameplayStatics.SpawnEmitterAtLocation(CasterActor, ParticleTemplate, SpawnLoc, Rotation, VectorHelper.ScaleOne(), bAutoDestroy) if CurEmitter ~= nil then table.insert(EmitterList, CurEmitter) end EffectSystemManager.UpdateEffectInstanceList(InstanceId, EffectId, EmitterList, CasterActor) return not table.isEmpty(EmitterList) end --------------------------------------------------------- 移除技能效果[外部接口] --------------------------------------------------------- ---移除指定Id对应的效果 function EffectSystemManager.RemoveEffectByInstanceId(InstanceId) if InstanceId == nil then UE.Log("[EffectSystemManager.RemoveEffectByInstanceId] InstanceId is nil") return end local PendingEffectInstances = {} for i, v in pairs(EffectSystemManager.ExistedEffectList) do if v.InstanceId == InstanceId then PendingEffectInstances[i] = v end -- 顺便把一些可能没正确去除的去掉 if v.CasterActor == nil or UE.IsValid(v.CasterActor) == false then EffectSystemManager.RemoveEffectInstance(v) EffectSystemManager.ExistedEffectList[i] = nil end end if not table.isEmpty(PendingEffectInstances) then for i, EffectInstance in pairs(PendingEffectInstances) do EffectSystemManager.RemoveEffectInstance(EffectInstance) EffectSystemManager.ExistedEffectList[i] = nil end else UE.LogError("[EffectSystemManager.RemoveEffectByInstanceId] Can't find EffectInstance with InstanceId: %d", InstanceId) end end ---移除指定Name的效果 function EffectSystemManager.RemoveEffectByEffectName(CasterActor, EffectName) if EffectName == nil then UE.Log("[EffectSystemManager.RemoveEffectByEffectName] EffectName is nil") return end local PendingEffectInstances = {} for i, v in pairs(EffectSystemManager.ExistedEffectList) do if v.EffectName == EffectName and v.CasterActor == CasterActor then PendingEffectInstances[i] = v end -- 顺便把一些可能没正确去除的去掉 if v.CasterActor == nil or UE.IsValid(v.CasterActor) == false then EffectSystemManager.ExistedEffectList[i] = nil end end if not table.isEmpty(PendingEffectInstances) then for i, EffectInstance in pairs(PendingEffectInstances) do EffectSystemManager.RemoveEffectInstance(EffectInstance) EffectSystemManager.ExistedEffectList[i] = nil end else UE.LogError("[EffectSystemManager.RemoveEffectByEffectName] Can't find EffectInstance with EffectName: "..tostring(EffectName)) end end ---移除指定Actor的所有效果 function EffectSystemManager.RemoveAllEffectOfActor(CasterActor) for i, v in pairs(EffectSystemManager.ExistedEffectList) do if v.CasterActor == CasterActor then EffectSystemManager.RemoveEffectInstance(v) EffectSystemManager.ExistedEffectList[i] = nil end end end ------------------------------------------- 移除效果实例[内部接口] ------------------------------------------- ---@param CasterActor AActor ---@param EffectInstance int function EffectSystemManager.RemoveEffectInstance(EffectInstance) if EffectInstance.EmitterList ~= nil and table.getCount(EffectInstance.EmitterList) > 0 then for _, Emitter in pairs(EffectInstance.EmitterList) do if Emitter ~= nil and UE.IsValid(Emitter) then Emitter:K2_DestroyComponent() end end end end return EffectSystemManager