local MoveComponent = class("MoveComponent") local FP=FP local LuaConst=LuaConst local LuaShoveManager = LuaShoveManager local Angle = FP.V2.Angle local LEVEL_ANCHOR_GROUP = -1 function MoveComponent:initialize(entity, pos, dir,skipFindFinalPos) local logicInitData = entity._logicInitData self.entity = entity if dir == FP.Const.Vector2_Zero then --零向量不允许用于初始化,否则会导致无法移动,直接重置为一个单位向量即可 --有时候服务器会发送零向量过来作为dir dir:Reset(0, 1) end self.InitPos = pos:cloneV2() --当前位置 self.LogicPos = skipFindFinalPos and pos:cloneV2() or LuaShoveManager.GetFinalInitPos(pos:cloneV2()) --上一帧的位置(这个并不是上一帧的位置,而是上一次调用设置位置接口前的位置,一帧可能调用多次设置位置接口) self.LastLogicPos = self.LogicPos:cloneV2() --真实的上一帧的位置,会在MoveSystem.OnLogicUpdate里更新 self.LastFrameLogicPos = self.LogicPos:cloneV2() --当前高度 self.LogicPosY = FP.newV1(0) LuaShoveManager.GetHeightByPos(self.LogicPos,self.LogicPosY) --上一帧的高度 self.LastLogicPosY = self.LogicPosY:cloneV1() --当前移动方向 self.LogicMoveDir = FP.newV2(0,0) --当前朝向 self.LogicRotateDir = dir:cloneV2() --上一逻辑帧的朝向 self.LastLogicRotateDir = dir:cloneV2() --摇杆方向 self.VirtualMoveDir = FP.newV2(0,1) -- 当前帧移动或者旋转的幅度是否是一半(摇杆幅度) self.IsHalfDir = false --当前帧想要移动的方向 self.WantMoveDir = FP.newV2(0,0) --当前帧想要旋转到的方向 self.WantRotateDir = FP.newV2(0,0) --当前帧想要到达的位置 self.WantPos = nil -- 一直吸引entity移动的引力点entity(被动的移动量) self.GravityForceEntity = nil -- 引力点产生的移动速度-- 每帧(0.05秒)的移动量 self.GravitySpeed = nil --加速度 self.Accelerate = FP.newV1(0) --角速度比例 self.MoveAngleSpeedPercent = FP.newV1(1) --挤压 self.ShoveForce = FP.newV2(0, 0) --忽略被其他人挤压 self.IgnoreOthersShoveMe = false --挤压优先级 self:ResetShovePriority(logicInitData._characterData) --骑马最低速度 self.RideMinSpeed = FP.newV1(3) -- 骑马时的最大奔跑速度 self.RideMaxSpeed = FP.newV1(0) -- 骑马时的最大旋转速度 self.RideMaxAngleSpeed = FP.newV1(0) -- 骑马时的当前速度 self.RideSpeedRate = FP.newV1(0) -- 骑马时的当前旋转速度 self.RideAngleSpeedRate = FP.newV1(0) -- 骑马给表现角度使用的值(即马头的摆动幅度) self.HorseExpAngle = FP.newV1(0) -- 驾驶车的是否踩油门 self.StepOnAccelerator = false self.PosAnchorList = {} self.ImmunoPosAnchorGroupDir = {} -- 免疫的秒点组字典 key = groupId , val = true self.PausePosAnchorGroupDir = {} -- 暂停的锚点组字典 key = groupId , val = true -- 自己的位置被某entity的位置 强制同步 self.ForceSyncPosEntity = nil self.ServerPos = FP.newV2(0, 0) end function MoveComponent:OnEntityInitialized(entity) self.hasIndependentShovePos = (entity.shoveOffsetZ and entity.shoveOffsetZ ~= 0) if self.hasIndependentShovePos then self.ShovePos = self.LogicPos:cloneV2() --物理碰撞、挤压的位置。2022年1月5日策划新增需求,物理碰撞位置要跟LogicPos有偏移 self.ShoveOffset = FP.newV2(0,0) self.ShoveOffset:Reset(self.LogicRotateDir):Mul(entity.shoveOffsetZ) self:RefreshShovePos() else self.ShovePos = self.LogicPos --如果没有碰撞位置偏移(没有shoveOffsetZ)就不需要独立碰撞位置,直接指向LogicPos即可 end self.asTargetOffsetZ = entity.components.base.CurModelData.ForwardOffsetAsTarget self.hasIndependentTargetPos = (self.asTargetOffsetZ and self.asTargetOffsetZ ~= 0) if self.hasIndependentTargetPos then self.posAsAttackTarget = self.LogicPos:cloneV2() --作为攻击目标时的位置。2022年1月6日策划新增需求,作为攻击目标时的位置要跟LogicPos有偏移 self.asTargetOffset = FP.newV2(0,0) self:RefreshPosAsAttackTarget() else self.posAsAttackTarget = self.LogicPos --如果没有offset则不需要独立的作为目标时的位置,直接指向LogicPos即可 end end function MoveComponent:SetShovePriority(priority) self.ShovePriority = priority end --设置标记,这个标记标明是否在非cmd的地方handle过move状态,目前只有SyncMoveManager.HandleEntityMove里会调(2021年2月2日) function MoveComponent:SetMoveStateHandledFlag(booleanValue) self.isMoveStateHandled = booleanValue end function MoveComponent:ResetShovePriority(characterData) self.ShovePriority = characterData.DefaultPriority or 0 end function MoveComponent:SetIgnoreOthersShoveMe(ignore) self.IgnoreOthersShoveMe = ignore end function MoveComponent:GetLogicPosY() return self.LogicPosY end --必须传入单位向量 function MoveComponent:SetLogicRotateDir(dir) return self.LogicRotateDir:Reset(dir) end --必须传入单位向量 function MoveComponent:SetLogicRotateDirXZ(x, z) return self.LogicRotateDir:Reset(x, z) end function MoveComponent:GetLogicRotateDir() return self.LogicRotateDir end function MoveComponent:SetLogicPos(pos,isInCliff, ignoreAnchor) if #self.PosAnchorList == 0 or (ignoreAnchor or self:PosAnchorCheck(pos)) then self.LogicPos:Reset(pos) LuaShoveManager.GetHeightByPos(self.LogicPos ,self.LogicPosY) self:SetIsInCliff(isInCliff) -- ↓因为位置变化一定会触发 LuaShoveManager的LuaShoveManager.SetGridDataByPriorityOfEntity -> MoveComponent:SetShovePosOffset -> MoveComponent:RefreshShovePos 的堆栈调用到RefreshShovePos,所以这里不用强制刷新以节省性能 -- if self.hasIndependentShovePos then -- self:RefreshShovePos() -- end --↑ if self.hasIndependentTargetPos then self:RefreshPosAsAttackTarget() end end end function MoveComponent:SetLogicPosXZ(x, z, isInCliff) if #self.PosAnchorList == 0 or self:PosAnchorCheckXZ(x,z) then self.LogicPos:Reset(x, z) LuaShoveManager.GetHeightByPos(self.LogicPos ,self.LogicPosY) self:SetIsInCliff(isInCliff) -- ↓因为位置变化一定会触发 LuaShoveManager的LuaShoveManager.SetGridDataByPriorityOfEntity -> MoveComponent:SetShovePosOffset -> MoveComponent:RefreshShovePos 的堆栈调用到RefreshShovePos,所以这里不用强制刷新以节省性能 -- if self.hasIndependentShovePos then -- self:RefreshShovePos() -- end -- ↑ if self.hasIndependentTargetPos then self:RefreshPosAsAttackTarget() end end end function MoveComponent:SetShovePosOffset(offsetV2) --因为LuaShoveManager会基于偏移计算ShovePos,所以这里直接使用那边产出的位置以节省性能 if not self.hasIndependentShovePos then self.hasIndependentShovePos = true self.ShovePos = self.LogicPos:cloneV2() self.ShoveOffset = FP.newV2(0,0) end self.ShoveOffset:Reset(offsetV2) self:RefreshShovePos() end function MoveComponent:RefreshShovePos() self.ShovePos:Reset(self.LogicPos) if self.ShoveOffset then self.ShovePos:Add(self.ShoveOffset) end end function MoveComponent:RefreshPosAsAttackTarget() if self.asTargetOffsetZ then self.posAsAttackTarget:Reset(self.LogicPos) self.asTargetOffset:Reset(self.LogicRotateDir):Mul(self.asTargetOffsetZ) self.posAsAttackTarget:Add(self.asTargetOffset) else logging.error(LTAG.ERROR, "HYW : 异常情况,self.asTargetOffsetZ为空,还试图RefreshPosAsAttackTarget") end end local _tempV1_old = FP.newV1(0) local _tempV1_new = FP.newV1(0) function MoveComponent:PosAnchorCheck(pos) local result = true for i,anchor in ipairs(self.PosAnchorList) do if not self.PausePosAnchorGroupDir[anchor.groupId] then --暂停的锚点组不参与判断 FP.V2.DistanceSqr(anchor.anchorPos, pos, _tempV1_new) if anchor.mode == LuaConst.EPosAnchorDistanceMode.NO_FARTHER then result = _tempV1_new <= anchor.chainLengthSqr if not result then --这一块代码的功能是允许超距离的靠近锚点 FP.V2.DistanceSqr(anchor.anchorPos, self.LogicPos, _tempV1_old) result = _tempV1_new <= _tempV1_old end elseif anchor.mode == LuaConst.EPosAnchorDistanceMode.NO_NEARER then result = _tempV1_new >= anchor.chainLengthSqr if not result then --这一块代码的功能是允许小于距离的远离锚点 FP.V2.DistanceSqr(anchor.anchorPos, self.LogicPos, _tempV1_old) result = _tempV1_new >= _tempV1_old end else logging.error(LTAG.ERROR, "HYW : 错误的 anchorMode @MoveComponent:PosAnchorCheck value : ", anchor.mode) end if not result then break end end end return result end function MoveComponent:PosAnchorCheckXZ(x, z) return self:PosAnchorCheck(FP.newV2(x, z)) end -- 设置免疫的锚点组 function MoveComponent:SetImmunoPosAnchorGroup(groupId, val) self.ImmunoPosAnchorGroupDir[groupId] = val if not val then BattleEventSystem.OnEntityImmunoPosAnchorRecover(self.entity) end end -- 设置暂停的锚点组 function MoveComponent:SetPausePosAnchorGroup(groupId, val) self.PausePosAnchorGroupDir[groupId] = val end --设置位置锚点 --位置锚点:可以限制单位的位置不远于或者不近于anchorPos位置 chainLength距离 的机制 function MoveComponent:AddPosAnchor(caster, anchorPos, chainLength, buffID, mode, groupId) -- groupId 对锚点的分组 if self.ImmunoPosAnchorGroupDir[groupId] then return end local exist = false for i,v in ipairs(self.PosAnchorList) do if v.caster == caster and v.anchorPos == anchorPos and v.chainLength == chainLength and v.buffID == buffID and v.mode == mode then exist = true break end end if not exist then local anchor = { caster = caster, anchorPos = anchorPos:cloneV2(), chainLength = chainLength, chainLengthSqr = FP.newV1(chainLength):Mul(chainLength), buffID = buffID, -- 当caster = "Level"时,这个值就是levelItemID mode = mode, -- EPosAnchorDistanceMode枚举值 groupId = groupId, } table.insert(self.PosAnchorList, anchor) end end function MoveComponent:AddPosAnchorByLevel(anchorPos, chainLength, buffID, mode) self:AddPosAnchor("Level", anchorPos, chainLength, buffID, mode, LEVEL_ANCHOR_GROUP) end function MoveComponent:RemovePosAnchorByLevel(buffID) self:RemovePosAnchor("Level", buffID) end function MoveComponent:RemovePosAnchor(caster, buffID) for i=#self.PosAnchorList, 1, -1 do local anchor = self.PosAnchorList[i] if (anchor.caster == caster) and (anchor.buffID == buffID or buffID == nil) then table.remove(self.PosAnchorList, i) end end end function MoveComponent:RemovePosAnchorByGroup(groupID) for i=#self.PosAnchorList, 1, -1 do local anchor = self.PosAnchorList[i] if anchor.groupId == groupID or groupID == nil then table.remove(self.PosAnchorList, i) end end end function MoveComponent:RemoveAllPosAnchor() table.clear(self.PosAnchorList) end function MoveComponent:RemoveAllPosAnchorButLevel() for i=#self.PosAnchorList, 1, -1 do local anchor = self.PosAnchorList[i] if (anchor.caster ~= "Level") then table.remove(self.PosAnchorList, i) end end end function MoveComponent:SetIsInCliff(isInCliff) if isInCliff ~= nil then if self.isInCliff ~= isInCliff then self.isInCliff = isInCliff if self.isInCliff then SystemManager.OnEnterCliff(self.entity) end end end end function MoveComponent:SetRestrictMoveToLogicRotateDir(value) self.restrictMoveToLogicRotateDir = value end function MoveComponent:SetWantDir(dir) if self.restrictMoveToLogicRotateDir then if dir ~= FP.Const.Vector2_Zero then self.WantMoveDir:Reset(self.LogicRotateDir) else self.WantMoveDir:Reset(dir) end else self.WantMoveDir:Reset(dir) if dir then self.WantMoveDir:Normalize() end end end function MoveComponent:SetHalfDir(isHalf) self.IsHalfDir = isHalf end function MoveComponent:SetWantRotateDir(dir) return self.WantRotateDir:Reset(dir) end --返回0-360的定点数 function MoveComponent:GetLastLogicRotateAngle() return FP.V2.AngleToRight360(self.LastLogicRotateDir) end --返回0-360的定点数 function MoveComponent:GetLogicRotateAngle() return FP.V2.AngleToRight360(self.LogicRotateDir) end function MoveComponent:SetCanEnterCliff(value) self.canEnterCliff = value end function MoveComponent:CanEnterCliff() if self.canEnterCliff then --InHitUp、InHitFlyMonster、InFlyFlyingMonster 状态下才能进入悬崖 local buffComponent = self.entity.components.buff return buffComponent:GetBuffUpdateData(LuaConst.SpecialBuffs.InHitUp) or buffComponent:GetBuffUpdateData(LuaConst.SpecialBuffs.InHitFlyMonster) or buffComponent:GetBuffUpdateData(LuaConst.SpecialBuffs.InFlyFlyingMonster) else return false end end function MoveComponent:ForbidMoveJoystick() self.forbidJoystick = true end function MoveComponent:CancelForbidMoveJoystick() self.forbidJoystick = false end function MoveComponent:SetStepOnAccelerator(useJoyStickType) if useJoyStickType == LuaConst.UseJoystickType.DriveAccelerator then self.StepOnAccelerator = true else self.StepOnAccelerator = false end end function MoveComponent:OnDead() self:RemoveAllPosAnchorButLevel() end function MoveComponent:SetForceSyncPosEntity(entity) self.ForceSyncPosEntity = entity end return MoveComponent