---@class BP_AvoidingSpikeStick_C:AActor ---@field TrailingFX UParticleSystemComponent ---@field StaticMesh1 UStaticMeshComponent ---@field StaticMesh UStaticMeshComponent ---@field SM_Stick UStaticMeshComponent ---@field RotatorCmp USceneComponent ---@field DefaultSceneRoot USceneComponent ---@field BeginRotSpeed float ---@field EndRotSpeed float --Edit Below-- local BP_AvoidingSpikeStick = { PlayMaxTime = 0.; PlayTime = 0.; StartTime = 0.; bIsPlaying = false; LastRotCmpRight = {X = 1, Y = 1, Z =1}; LastRotCmpForward = {X = 1, Y = 1, Z =1}; CenterPos = {X = 0, Y = 0, Z =0}; StickZ = 0; }; function BP_AvoidingSpikeStick:ReceiveBeginPlay() self.SuperClass.ReceiveBeginPlay(self); self:InitTrap() end function BP_AvoidingSpikeStick:InitTrap() self.PlayMaxTime = MiniGameConfig.MiniGameInfo[MiniGameConfig.MiniGameType.AvoidingSpikeStick].MaxGameTime self.CenterPos = self.RotatorCmp:K2_GetComponentLocation() self.StickZ = self.SM_Stick:K2_GetComponentLocation().Z self.LastRotCmpForward = self.RotatorCmp:GetForwardVector() self.LastRotCmpRight = self.RotatorCmp:GetRightVector() if UGCGameSystem.IsServer() then -- UGCSystemLibrary.BindBeginOverlapFunc(self.SM_Stick, self.StickOverlapPlayer, self) else self.HitParticle = UE.LoadObject(MiniGameConfig.MiniGameInfo[MiniGameConfig.MiniGameType.AvoidingSpikeStick].GameParam.HitParticlePath) end end function BP_AvoidingSpikeStick:StartPlay() if self.bIsPlaying == false then self.bIsPlaying = true self.StartTime = UGCGameSystem.GameState:GetServerGameTime() DOREPONCE(self, "StartTime") DOREPONCE(self, "bIsPlaying") end end function BP_AvoidingSpikeStick:StopPlay() self.bIsPlaying = false end function BP_AvoidingSpikeStick:ReceiveTick(DeltaTime) self.SuperClass.ReceiveTick(self, DeltaTime); if self.bIsPlaying then local NowServerTime = UGCGameSystem.GameState:GetServerGameTime() local TimeDifference = NowServerTime - self.StartTime local NowSpeed = self.BeginRotSpeed + (self.EndRotSpeed - self.BeginRotSpeed) * TimeDifference / self.PlayMaxTime local RotYaw = TimeDifference * (NowSpeed + self.BeginRotSpeed) / 2 if UGCGameSystem.IsServer() then self.RotatorCmp:K2_SetRelativeRotation({Roll = 0., Pitch = 0., Yaw = RotYaw}) self:CheckStickHitPlayer() --local HitResult = self.SM_Stick:K2_SetRelativeRotation({Roll = 0., Pitch = 90., Yaw = RotYaw}, true) --local HitActor = HitResult.Actor:Get() --if HitActor then -- UGCLogSystem.Log("[BP_AvoidingSpikeStick_ReceiveTick] ActorName:%s", KismetSystemLibrary.GetObjectName(HitActor)) -- if HitActor.PlayerKey then -- UGCEventSystem.SendEvent(EventEnum.StickOverlapPlayer, HitActor.PlayerKey) -- end -- --end else self:CheckStickCrossingLocalPlayer() self.RotatorCmp:K2_SetRelativeRotation({Roll = 0., Pitch = 0., Yaw = RotYaw}) end end end --- 检测两帧棍棒扫过的区域内是否有玩家 function BP_AvoidingSpikeStick:CheckStickHitPlayer() local RotCmpForward = self.RotatorCmp:GetForwardVector() local RotCmpRight = self.RotatorCmp:GetRightVector() RotCmpForward.Z = 0. local AllPawn = UGCGameSystem.GetAllPlayerPawn() for i, PlayerPawn in pairs(AllPawn) do if PlayerPawn.PlayerKey then if self:CheckCrossingPlayer(PlayerPawn, RotCmpForward, RotCmpRight) then local PawnPos = PlayerPawn:K2_GetActorLocation() -- 通过判断棍棒高度是否在玩家当前的高度区间内进行判断是否击中 if PawnPos.Z - 88.0 < self.StickZ and PawnPos.Z + 88.0 > self.StickZ then -- UGCLogSystem.Log("[BP_AvoidingSpikeStick_ReceiveTick] PlayerKey:%s Hit", tostring(PlayerPawn.PlayerKey)) UGCEventSystem.SendEvent(EventEnum.StickOverlapPlayer, PlayerPawn.PlayerKey) end end end end self.LastRotCmpForward = RotCmpForward self.LastRotCmpRight = RotCmpRight end --- 判断杆子越过本地玩家 function BP_AvoidingSpikeStick:CheckStickCrossingLocalPlayer() local RotCmpForward = self.RotatorCmp:GetForwardVector() local RotCmpRight = self.RotatorCmp:GetRightVector() RotCmpForward.Z = 0. local LocalPawn = UGCSystemLibrary.GetLocalPlayerPawn() if UE.IsValid(LocalPawn) then if self:CheckCrossingPlayer(LocalPawn, RotCmpForward, RotCmpRight) then self:ClientStickCrossingLocalPlayerEffect() end end self.LastRotCmpForward = RotCmpForward self.LastRotCmpRight = RotCmpRight end --- 客户端扫过本地玩家触发特效或者音效 function BP_AvoidingSpikeStick:ClientStickCrossingLocalPlayerEffect() SoundSystem.PlaySound(SoundSystem.ESound.BreakWind) end --- 判断杆子越过玩家 function BP_AvoidingSpikeStick:CheckCrossingPlayer(PlayerPawn, RotCmpForward, RotCmpRight) local PawnPos = PlayerPawn:K2_GetActorLocation() local ToPlayerDir = VectorHelper.Sub(PawnPos, self.CenterPos) ToPlayerDir.Z = 0. -- 判断该玩家是否在两帧划过的扇形内 -- 通过判断到玩家的向量与两帧的向前向量的余弦均为正 并且 该向量与两帧的向右向量的两余弦值的积为负 if VectorHelper.CosineValue(ToPlayerDir, RotCmpForward) > 0 and VectorHelper.CosineValue(ToPlayerDir, self.LastRotCmpForward) > 0 and VectorHelper.CosineValue(ToPlayerDir, RotCmpRight) * VectorHelper.CosineValue(ToPlayerDir, self.LastRotCmpRight) < 0 then return true end return false end function BP_AvoidingSpikeStick:StickOverlapPlayer(OverlappedComponent, OtherActor, OtherComp, OtherBodyIndex, bFromSweep, SweepResult) if OtherActor and OtherActor.PlayerKey then -- UGCEventSystem.SendEvent(EventEnum.StickOverlapPlayer, OtherActor.PlayerKey) end end --[[ function BP_AvoidingSpikeStick:ReceiveEndPlay() self.SuperClass.ReceiveEndPlay(self); end --]] function BP_AvoidingSpikeStick:GetReplicatedProperties() return "StartTime", "bIsPlaying" end --[[ function BP_AvoidingSpikeStick:GetAvailableServerRPCs() return end --]] function BP_AvoidingSpikeStick:ClientHitPlayerEffect(Pos) UGCLogSystem.Log("[BP_AvoidingSpikeStick_ClientHitPlayerEffect]") if self.HitParticle then GameplayStatics.SpawnEmitterAtLocation(self, self.HitParticle,Pos, { Roll = 0, Pitch = 0, Yaw = 0 }, { X = 0.6, Y = 0.6, Z = 0.6 },true) SoundSystem.PlaySound(SoundSystem.ESound.Impact) end end return BP_AvoidingSpikeStick;