local ExpMissile = class("ExpMissile") local private = {} local ExpMoveMissileSystem = require "Battle/Expression/ExpMissile/ExpMoveMissileSystem" local tonumberV2=FP.tonumberV2 local tonumberV1=FP.tonumberV1 local newV1=FP.newV1 local Vector3_New=Vector3.New local Quaternion_AngleAxis=Quaternion.AngleAxis local Mathf_Sqrt=Mathf.Sqrt local Mathf_Atan=Mathf.Atan local Mathf_Rad2Deg=Mathf.Rad2Deg local Vector3_up=Vector3.up local Vector3_left=Vector3.left local AuxUtil=AuxUtil local AppLoadingProxy=AppLoadingProxy local math_atan=math.atan local math_pi=math.pi local MissileShapeType=LuaConst.MissileShapeType local SystemManager = SystemManager local ExpEffectSystem = require "Battle/Expression/ExpEntity/System/ExpEffectSystem" local PSEffectPath = ConstAssetPathConfig.PsGroupPath local RedWarningPathDic = { [MissileShapeType.Circle] = ConstAssetPathConfig.AssetPath205, [MissileShapeType.Box] = ConstAssetPathConfig.AssetPath206, --[MissileShapeType.Sector] = ConstAssetPathConfig.AssetPath205, -- 策划暂时没有扇形的需求,也没有资源 。 } local ShowEffectCampType = { All = 0, -- 全部人可见 FriendAndMe = 1, -- 只有我们方能看到 Enemy = 2,-- 只有敌人能看到 } function ExpMissile:initialize(missile) self.Missile = missile -- 子弹的逻辑对象 self.Trans = AuxUtil.GetEmptyObjectTransform(AuxUtil.GetGlobalEffectRoot()) self.PSFollowConfig = nil -- 特效跟随配置 if missile.MissileData.AimNodeSet then local aimTable = missile.MissileData.AimNodeSet local x = aimTable[1] local y = aimTable[2] local z = aimTable[3] self.TrackerOffset = Vector3_New(x, y, z) else self.TrackerOffset = Vector3_New(0, 0, 0) end --变化数据 self.TotalTime = tonumberV1(missile.TotalTime )/ 1000 --总时间 self.ExpressionTimer = 0 -- 当前运行时间 单位:s self.MoveVec = Vector3_New(1, 1, 1) -- 子弹的移动方向 self.Pos = Vector3_New(0, 0, 0) -- 子弹的位置 self.SpeedY = tonumberV1(missile.SpeedY) --抛物线 self.InitPos = nil --抛物线时记录的发射位置 self.ParabolaParam_a = 0 -- 普通抛物线:y=a*x^2+bx+c 参数a self.ParabolaParam_b = 0 --普通抛物线:y=a*x^2+bx+c 参数b self.FireAngle = 0 --使用抛物线,子弹的水平发射角度 self.FireAngleSpeed = 0 -- 使用抛物线,子弹旋转的角速度 ,由于角度随机,所以这个角速度也随机 --被击音效 self.HitTargetSoundPlayCount = 0 self.EffectPlayID = nil --子弹特效本身的句柄ID end -- offsetx, offsetz 是表现偏差 (军团怪多个子模型的偏差值) function ExpMissile:OnFire(paramData, offsetx, offsetz) local missile = self.Missile self.ExpressionTimer = 0 self.TotalTime = tonumberV1(missile.TotalTime )/ 1000 -- 秒 self.SpeedY = tonumberV1(missile.SpeedY) self:SetInitData(paramData ,false, offsetx, offsetz) local attacker = missile.Attacker local missileData = missile.MissileData if missileData.NeedShowEnemyRange and LevelManager.IsShowEnemyMissile() and LogicUpdateManager._curEntity and CampManager.IsEnemyEntity(LogicUpdateManager._curEntity, missile.Attacker) then local canShow = true if LevelManager.IsSyncWorldBossLevel() and LevelPlayerManager.IsSameGuild(LogicUpdateManager._curEntity, missile.Attacker) then canShow = false end if canShow then local warningPath = RedWarningPathDic[missileData.ShapeType] self.enemyRangePsID = AuxUtil.PlayDynamicObject(warningPath, -1, self.Trans, 0) AuxUtil.SetDynamicObjectPos(self.enemyRangePsID, tonumberV1(missileData.OffsetX),0.1,tonumberV1(missileData.OffsetZ)) if missileData.ShapeType == MissileShapeType.Circle then local scale = tonumberV1(missile:GetShapeParam1()) * 2 -- 按特效资源尺寸大小 AuxUtil.SetDynamicObjectScale(self.enemyRangePsID, scale, 1, scale) else local scaleX = tonumberV1(missile:GetShapeParam1()) -- 按特效资源尺寸大小 local scaleZ = tonumberV1(missile:GetShapeParam2()) -- 按特效资源尺寸大小 AuxUtil.SetDynamicObjectScale(self.enemyRangePsID, scaleX, 1, scaleZ) end end end if missileData.PSMissileResource ~= "" then local canShowEffect = true local isEnemy = CampManager.IsEnemyCamp(attacker:GetCampID(),LevelPlayerManager.MainEntityCampID) local showType = missileData.ShowEffectCampType or 0 -- bug:子弹表中根本没有ShowEffectCampType,而是EffectCamp 。(目前只有军团战用了)策划说不改了。 if (showType == ShowEffectCampType.FriendAndMe and isEnemy) or (showType == ShowEffectCampType.Enemy and isEnemy == false) then canShowEffect = false end if canShowEffect then --实现特效 local psResource = attacker.components.replace:GetReplaceSkillPath(missile.SkillID, missileData.PSMissileResource) if missile:IsHook() then local psResourceCombined = string.format(PSEffectPath, psResource) self.EffectPlayID = ExpEffectSystem.PlayHookEffect(missile.hookStartEntity.ExpEntity, psResourceCombined) self:OnHookFire(missile.hookEntity) elseif self.Trans then local timeScale = attacker.components.timeScale:GetCurMissileTimeScale() local layer = attacker.ExpEntity.components.model.Layer local effectAsPlayerSelf = ExpUpdateManager.IsEffectAsPlayerSelf(attacker) local characterType = attacker._logicInitData.CharacterType --玩家自己也要消耗子弹特效的额度,但是不受额度限制直接给予播放 local expRenderEntity if LevelManager.CurLevelFunc.exp.missileRenderEntity then -- 目前修复 国服的200版本PC包上的副将 离手子弹特效 expRenderEntity = LevelManager.CurLevelFunc.exp.missileRenderEntity if attacker.ExpEntity.RenderEntity ~= nil then local pos, rotate pos, rotate = attacker.ExpEntity.RenderEntity.Transform:GetPositionAndRotation(pos, rotate) LevelManager.CurLevelFunc.exp.missileRenderEntity.Transform:SetPositionAndRotation(pos,rotate) end else expRenderEntity = attacker.ExpEntity:GetRenderEntity() end self.EffectPlayID = AuxUtil.PlayMissileFollowEffect(expRenderEntity, self.Trans, psResource, effectAsPlayerSelf, characterType, layer, self.TotalTime, tonumberV1(timeScale), LevelManager.IsSyncLevel()) end end end --追踪弹 if missile.IsTracker then if missile.FireRotate then local dx, dz = tonumberV2(missile.MoveDir) local forward = Vector3_New(dx, 0, dz) local right = Vector3_New(dz, 0, -dx) local rx, rz = tonumberV2(missile.FireRotate) local dir = Quaternion_AngleAxis(rx, right) * forward dir = Quaternion_AngleAxis(rz, Vector3_up) * dir local rate = forward.y / Mathf_Sqrt(dir.x * dir.x + dir.z * dir.z) rate = rate > 2 and 2 or rate --避免无限大 rate = rate < -2 and -2 or rate self.TrackerSpeedY = rate * tonumberV1(missileData.TrackFlySpd) else self.TrackerSpeedY = 0 end --print("TrackerSpeedY", self.TrackerSpeedY) end end function ExpMissile:ExpUpdate(deltaTime) local missile = self.Missile if missile == nil then logging.error(LTAG.ERROR , "[战斗程序] :发现了表现子弹找不到逻辑子弹,表现子弹停止更新 ! 只报错不阻挡 ") return end local missileData = missile.MissileData if missileData.PSMissileResource == "" and not missileData.NeedShowEnemyRange then --gws :优化子弹,当没有子弹表现特效的时候 ,表现子弹是没必要移动的(可以省一些计算量) return end local timeScale = tonumberV1(missile.Attacker.components.timeScale:GetCurMissileTimeScale()) local addTime = deltaTime * timeScale if self.IsTracker then ExpMoveMissileSystem.UpdateExpressionPosAndDir(self,deltaTime) else self.ExpressionTimer = self.ExpressionTimer + addTime if self.ExpressionTimer <= self.TotalTime then ExpMoveMissileSystem.UpdateExpressionPosAndDir(self,deltaTime) end end end function ExpMissile:OnCancelled(missile) if self.Missile then --设置特效隐藏 ExpSystemManager.OnMissileCancled(self) if self.EffectPlayID and self.EffectPlayID ~=0 then --means has try effect played if missile:IsHooked() then ExpEffectSystem.StopHookEffect(self.EffectPlayID) else AuxUtil.StopMissileFollowEffect(self.EffectPlayID) end end if self.Trans then if GameAppInfo.IsVersionGreaterThan114000() then SoundUtil.DisposeSoundObj(self.Trans) end AuxUtil.ReturnEmptyObject(self.Trans) self.Trans = nil end self.Missile = nil self.TotalTime = 0 self.ExpressionTimer = 0 self.EffectPlayID = nil end if self.enemyRangePsID then AuxUtil.StopDynamicObject(self.enemyRangePsID) self.enemyRangePsID = nil end end function ExpMissile:WaitCancelled(missile) if not missile.MissileData.HideDelayFrameIsShow then ---:特效隐藏 if self.EffectPlayID and self.EffectPlayID ~=0 then --means has try effect played AuxUtil.StopMissileFollowEffect(self.EffectPlayID) end self.EffectPlayID = nil end end function ExpMissile:SetTrackerForceY(forceY) if forceY then if forceY > -900 then -- 一个无行走的地方,则子弹保持原高度 self.TrackerForceY = forceY end else local missile = self.Missile if missile.TrackerTarget.ExpEntity:GetRenderEntity() then local slotInt = LuaConst.HumanEquipSlot[missile.MissileData.AimNode] self.TrackerForceY = missile.TrackerTarget.ExpEntity:GetEquipSlotHeight(slotInt ,self.Pos.y) self.TrackerForceY = self.TrackerForceY + self.TrackerOffset.y end end end function ExpMissile:SetInitData(paramData,isForOptimize, offsetx, offsetz) if isForOptimize then -- 预加载 self.Trans:SetPosition(-1000, -1000, 0) self.MoveVec = Vector3_New(0, 0, 0) return end local missile = self.Missile local missileData = missile.MissileData local px, pz = tonumberV2(missile.PosOffset) self.PosOffset = Vector3_New(px, tonumberV1(paramData.OffsetY), pz) --给绑定子弹使用的 --非绑定的子弹 px, pz = tonumberV2(missile.Pos) if offsetx and offsetz then -- 军团怪的子模型的偏移 px = px + offsetx pz = pz + offsetz end local py = tonumberV1(paramData.PosY) + tonumberV1(paramData.OffsetY) local pos = Vector3_New(px, py, pz) local dx, dz = tonumberV2(missile.MoveDir) local dir = Vector3_New(dx, 0, dz) if missile.IsTracker and missile.FireRotate then -- 只有追踪子弹才需要再次的旋转 ,但只旋转x轴 ,y轴不能旋转 dx, dz = tonumberV2(missile.FireDir) local forward = Vector3_New(dx, 0, dz) local right = Vector3_New(dz, 0, -dx) local rx, rz = tonumberV2(missile.FireRotate) dir = Quaternion_AngleAxis(rx, right) * forward --dir = Quaternion_AngleAxis(rz, Vector3_up) * dir --但只旋转x轴 ,y轴不能旋转 ;y轴已经在逻辑中旋转过了 end self.Pos = pos self.MoveVec = dir self.Trans:SetPosition(pos.x,pos.y,pos.z) if self.SpeedY ~= 0 then local right = Vector3_New(dz, 0, -dx) local angle = private.GetAngleXAxis( tonumberV1(missileData.MoveSpeed),self.SpeedY) dir = Quaternion_AngleAxis(angle, -right) * dir end self.Trans.forward = dir if missile.IsTracker then if missile.InFreeFly then if missileData.FreeFlyType == 0 then --静止 self.MoveVec = Vector3_New(0, 0, 0) end end elseif missileData.MoveType == LuaConst.MissileMoveType.Parabola then --抛物线子弹 local flytime = self.TotalTime local px, pz = tonumberV2(missile.InitPos) if offsetx and offsetz then -- 军团怪的子模型的偏移 px = px + offsetx pz = pz + offsetz end self.InitPos = Vector3_New(px, pos.y, pz) --抛物线参数 local s = flytime * tonumberV1(missileData.MoveSpeed) px, pz = tonumberV2(paramData.ParabolaTargetPos) local targetY = AuxUtil.GetHigh(px, tonumberV1(paramData.PosY), pz) local dh = targetY - pos.y if missileData.ParabolaHight == 0 then self.ParabolaParam_b = 0 self.ParabolaParam_a = dh / (s * s) self.FireAngle = 0 self.FireAngleSpeed = 0 else local h = missileData.ParabolaHight if dh > 0.5 then h = dh + h end local paramb = (s + Mathf_Sqrt(s * s - (s * s * dh / h))) / (s * s / (2 * h)) local parama = -(paramb * paramb) / (4 * h) self.ParabolaParam_a = parama self.ParabolaParam_b = paramb if missileData.CanRotate then --根据抛物线的切线公式得于x轴的角度为:angle = Mathf.Atan(2 * a * x + b) * Mathf.Rad2Deg local fireAngle = Mathf_Atan(paramb) * Mathf_Rad2Deg --x = 0为入射角度 local endAngle = Mathf_Atan(2*parama*s + paramb) * Mathf_Rad2Deg --x =s 时为结束角度 self.FireAngle = fireAngle self.FireAngleSpeed = (endAngle - fireAngle) / flytime UIUtils.RotateByLeft(self.Trans,fireAngle) end end end end -- 获取 绕 x 的角度 function private.GetAngleXAxis(x,y) local tanRadVal = y / x local radVal = math_atan(tanRadVal) local angleVal = radVal/ math_pi * 180 return angleVal end --↓↓↓钩子特性支持↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ function ExpMissile:TryShowHookEffect(hookEntity) if self.EffectPlayID then ExpEffectSystem.TryHookEntity(self.EffectPlayID, hookEntity.ExpEntity) end end function ExpMissile:OnHookFire(hookEntity) if self.Missile and self.Missile:IsHooked() then --存在还没有Fire,就建立了钩子的情况,所以在Fire的时候再次尝试播放钩子特效 self:TryShowHookEffect(hookEntity) end end function ExpMissile:OnEstablishHook() if self.Missile then local hookEntity = self.Missile.hookEntity if hookEntity then self:TryShowHookEffect(hookEntity) end end end function ExpMissile:SetPsEffectScale(scale) self.Trans:SetLocalScale(scale,scale,scale) self.PosOffset.y = self.PosOffset.y + (scale / 2) end return ExpMissile