2025-01-04 23:00:19 +08:00

399 lines
15 KiB
Lua
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---@class MovingActorsManager
---@field FrameTime float
---@field Frame float
---@type MovingActorsManager
local MovingActorsManager = {};
---@type float 一秒钟发多少次 RPC
MovingActorsManager.Frame = 3.;
MovingActorsManager.FrameTime = 1 / MovingActorsManager.Frame;
---@type table<string, AActor> 所有需要移动的 Actor
MovingActorsManager.ActorMap = {};
---@type table<string, MovingActorInfo> 同步用的表
MovingActorsManager.ActorNameMap = { };
---@type boolean 是否启用移动
MovingActorsManager.EnableMoving = false;
---@type boolean 是否需要跟服务器校准
MovingActorsManager.bAdjustServer = false;
---@type AActor 拥有者
MovingActorsManager.Owner = nil;
---@type table<string, float> 缓存服务器注册但是客户端未注册的 Actor
MovingActorsManager.CacheActorInfo = {};
---@type float 服务器客户端延迟,默认值较大,然后获取的值会比该值小
MovingActorsManager.Delay = 25;
MovingActorsManager.IsAlready = false;
--- 初始化,客户端,服务器同时进行
---@param InOwner AActor
function MovingActorsManager:Init(InOwner)
--- 计算客户端延迟
self.Owner = InOwner;
-- 直接开启
self.EnableMoving = true;
-- 创建一个 BP_MoveActorManager等待同步之后再一起注册需要保证是同步的
if not self:HasAuthority() then
local CurrTime = KismetSystemLibrary.GetGameTimeInSeconds(self.Owner)
local PC = STExtraGameplayStatics.GetFirstPlayerController(UGCGameSystem.GameState);
self:ClientSendRPC("AllReady", PC.PlayerKey, CurrTime);
end
end
MovingActorsManager.ClientTimes = {}
---@param InTime float 客户端准备就绪的时间
function MovingActorsManager:AllReady(InPlayerKey, InTime)
if self:HasAuthority() then
local CurrTime = KismetSystemLibrary.GetGameTimeInSeconds(self.Owner);
local Time = CurrTime - InTime;
UGCLogSystem.Log("[MovingActorsManager:ClientInit] 当前服务器,客户端延迟:%s", CurrTime - InTime);
self.ClientTimes[InPlayerKey] = Time;
self:ServerSendRPC("AllReady", nil, self.ClientTimes);
self.IsAlready = true;
else
self.ClientTimes = InTime;
local PC = STExtraGameplayStatics.GetFirstPlayerController(UGCGameSystem.GameState);
local Time = self.ClientTimes[PC.PlayerKey]
self.Delay = Time;
self.IsAlready = true;
end
end
---@param InActor AActor
---@param InDelay float
---@param IsMove bool
---@param InDir FVector
---@param InRot FRotator
function MovingActorsManager:RegisterMovingActorByMoveDirection(InActor, InDelay, IsMove, InDir, InRot)
if not UE.IsValid(InActor) then return false; end
local Name = UE.GetName(InActor);
local InActorPos = VectorHelper.ToLuaTable(InActor:K2_GetActorLocation());
local InActorRot = VectorHelper.RotToLuaTable(InActor:K2_GetActorRotation());
local CurrTime = KismetSystemLibrary.GetGameTimeInSeconds(self.Owner);
if self:HasAuthority() then
-- 判断当前是否客户端能否注册,不能注册,返回
local ActualTime = CurrTime - self.Delay - 0.5;
if ActualTime < 0 then return false; end
end
---@type MovingActorInfo
local Info = {
Delay = InDelay,
StartPos = InActorPos,
StartRot = InActorRot,
EndPos = nil,
CurrPos = VectorHelper.ToLuaTable(InActorPos),
DeltaPos = nil, -- 实时计算结果
StartMoveTime = InDelay + CurrTime,
TotalMoveTime = 0, -- 实时计算结果
MoveTime = 0,
IsMove = IsMove,
WaitServerMove = false,
MoveDirection = InDir,
Rotation = InRot,
Func = nil,
};
if self.ActorMap[Name] ~= nil then
Info.StartMoveTime = self.ActorNameMap[Name].StartMoveTime;
Info.WaitServerMove = true;
else
self.ActorMap[Name] = InActor;
end
self.ActorNameMap[Name] = Info;
if self:HasAuthority() then
local TimeTable = {
ServerTime = CurrTime;
ServerStartTime = Info.StartMoveTime;
Delay = InDelay;
MoveDir = Info.MoveDirection,
Rotation = Info.Rotation
}
UGCLogSystem.LogTree(string.format("[MovingActorsManager:RegisterMovingActor] TimeTable = "), TimeTable);
self:ServerSendRPC("ClientRegisterMovingActor", InActor, TimeTable);
else
-- 说明当前传过来了
if self.CacheActorInfo[Name] ~= nil then
self.ActorNameMap[Name].StartMoveTime = self.CacheActorInfo[Name].ClientMoveTime;
self.CacheActorInfo[Name] = nil;
self.ActorNameMap[Name].WaitServerMove = true; -- 此时已经不准了
UGCLogSystem.Log("[MovingActorsManager:RegisterMovingActor] 当前 Actor: %s 的移动是不准确的了", Name);
end
end
end
--- 通过控制判断当前移动,客户端服务器一起注册,然后一起执行
--- 注意:
--- 1.如果是在一开始就需要注册的:一定要先让服务器先注册(什么时候注册无所谓,但是要至少提前大约 23-25 秒注册),客户端后注册。且开始的时候一定要有延迟,以服务器为准
--- 2.如果是在后面注册的,那么不用担心,服务器比客户端提前 2-3 秒即可。且延迟可以随意加;还是以服务器为准。
--- S & C
---@param InActor AActor
---@param InDelay float
---@param Func fun(Actor: AActor, InDeltaTime:float):bool, FVector, FRotator return是否移动移动方向移动旋转是否显示 移动函数,可以每帧进行控制,但是最好不要这么做,因为每改变一次,都会发一次 RPC
function MovingActorsManager:RegisterMovingActor(InActor, InDelay, Func)
if not UE.IsValid(InActor) then return false; end
local Name = UE.GetName(InActor);
local InActorPos = VectorHelper.ToLuaTable(InActor:K2_GetActorLocation());
local InActorRot = VectorHelper.RotToLuaTable(InActor:K2_GetActorRotation());
local CurrTime = KismetSystemLibrary.GetGameTimeInSeconds(self.Owner);
-- 在客户端上进行一下处理
--if self:HasAuthority() then
-- -- 判断当前是否客户端能否注册,不能注册,返回
-- local ActualTime = CurrTime - self.Delay - 0.5;
-- if ActualTime < 0 then return false; end
--end
---@type MovingActorInfo
local Info = {
Delay = InDelay,
StartPos = InActorPos,
StartRot = InActorRot,
EndPos = nil,
CurrPos = VectorHelper.ToLuaTable(InActorPos),
DeltaPos = nil, -- 实时计算结果
StartMoveTime = InDelay + CurrTime,
TotalMoveTime = 0, -- 实时计算结果
MoveTime = 0,
IsMove = false,
WaitServerMove = false,
MoveDirection = nil,
Rotation = nil,
--IsShow = true,
Func = Func,
};
-- 此时是客户端先注册了,一定是正确的
if self.ActorMap[Name] ~= nil then
Info.StartMoveTime = self.ActorNameMap[Name].StartMoveTime;
UGCLogSystem.Log("[MovingActorsManager:RegisterMovingActor] zhixing")
Info.WaitServerMove = true;
end
self.ActorMap[Name] = InActor;
self.ActorNameMap[Name] = Info;
UGCLogSystem.LogTree("[MovingActorsManager:RegisterMovingActor] self.ActorNameMap = ", self.ActorNameMap);
if self:HasAuthority() then
local TimeTable = {
ServerTime = CurrTime;
ServerStartTime = Info.StartMoveTime;
Delay = InDelay;
}
UGCLogSystem.LogTree(string.format("[MovingActorsManager:RegisterMovingActor] TimeTable = "), TimeTable);
self:ServerSendRPC("ClientRegisterMovingActor", InActor, TimeTable);
else
-- 说明当前传过来了
if self.CacheActorInfo[Name] ~= nil then
self.ActorNameMap[Name].StartMoveTime = self.CacheActorInfo[Name].ClientExecTime;
self.ActorNameMap[Name].WaitServerMove = true; -- 此时已经不准了
self.CacheActorInfo[Name] = nil;
UGCLogSystem.Log("[MovingActorsManager:RegisterMovingActor] 当前 Actor: %s 的移动是不准确的了", Name);
end
end
return true;
end
--- C 客户端注册移动 Actor
---@param InTimeTable table 服务器执行时间
---@param InActor AActor 注册的 Actor
function MovingActorsManager:ClientRegisterMovingActor(InActor, InTimeTable)
local Name = UE.GetName(InActor);
local CurrTime = KismetSystemLibrary.GetGameTimeInSeconds(self.Owner);
UGCLogSystem.LogTree(string.format("[MovingActorsManager:ClientRegisterMovingActor] InTimeTable = "), InTimeTable)
--- 说明还没有值
if self.ActorMap[Name] == nil then
self.ActorMap[Name] = InActor;
self.ActorNameMap[Name] = {};
end
self.ActorNameMap[Name].StartMoveTime = InTimeTable.ServerStartTime - self.Delay + self:GetPing();
self.ActorNameMap[Name].WaitServerMove = true;
self.CacheActorInfo[Name] = {
ExecTime = InTimeTable.ServerStartTime,
ServerTime = InTimeTable.ServerTime;
ClientExecTime = InTimeTable.ServerStartTime - self.Delay + self:GetPing();
};
end
--- S & C
---@param InActor AActor 取消注册的 Actor
---@param RemoveCompletely boolean 是否是彻底取消
function MovingActorsManager:UnregisterMovingActor(InActor, RemoveCompletely)
local Name = UE.GetName(InActor);
UGCLogSystem.Log("[MovingActorsManager:UnregisterMovingActor] Actor: %s, 是否完全移除?:%s", Name, tostring(RemoveCompletely));
local Info = self.ActorNameMap[Name];
if Info ~= nil then
Info.WaitServerMove = false;
end
if RemoveCompletely then
self.ActorNameMap[Name] = nil;
self.ActorMap[Name] = nil;
end
if self:HasAuthority() then
self:ServerSendRPC("UnregisterMovingActor", InActor, RemoveCompletely);
end
end
---@param InDeltaTime float 当前帧执行的时间
function MovingActorsManager:Tick(InDeltaTime)
self:MoveAllActors1(InDeltaTime);
-- 同步位置
self:SyncLocation(InDeltaTime);
end
MovingActorsManager.TempSyncData = {
CountTime = 0.;
};
---@param InDeltaTime float
function MovingActorsManager:SyncLocation(InDeltaTime)
if not self.bAdjustServer then return end
if not self:HasAuthority() then return end
if table.isEmpty(self.ActorMap) then return end
if not self.EnableMoving then return end
if not self.IsAlready then return end
local CurrTime = KismetSystemLibrary.GetGameTimeInSeconds(self.Ownere);
self.TempSyncData.CountTime = self.TempSyncData.CountTime + InDeltaTime;
if self.TempSyncData.CountTime > self.FrameTime then
self.TempSyncData.CountTime = math.fmod(self.TempSyncData.CountTime, self.FrameTime);
-- 执行,组装 table
local Table = {};
for Name, Info in pairs(self.ActorNameMap) do
Table[Name] = {
Location = Info.CurrPos,
Rotation = Info.CurrRot;
ServerTime = CurrTime,
}
end
-- 发送过去
self:ServerSendRPC("Client_SyncLocation", Table);
end
end
function MovingActorsManager:Client_SyncLocation(InTable)
if table.isEmpty(self.ActorNameMap) then
return;
end
local CurrTime = KismetSystemLibrary.GetGameTimeInSeconds(self.Owner);
UGCLogSystem.LogTree(string.format("[MovingActorsManager:Client_SyncLocation] 同步, InTable = "), InTable)
-- 对比当前的位置
UGCLogSystem.LogTree(string.format("[MovingActorsManager:Client_SyncLocation] 客户端时间:%s, self.ActorNameMap = ", tostring(CurrTime)), self.ActorNameMap);
--UGCLogSystem.Log("[MovingActorsManager:Client_SyncLocation] Server - Client = %s", tostring(InTable.ServerTime - CurrTime));
end
MovingActorsManager.CurrMoveTime = 0.;
MovingActorsManager.bServerEverTick = true;
--- 重置一个 Actor
--- S
function MovingActorsManager:ResetActor(InActor)
local Name = UE.GetName(InActor);
local Actor = self.ActorMap[Name]
if Actor == nil then return end
Actor:K2_SetActorLocation(self.ActorNameMap[Name].StartPos);
Actor:K2_SetActorRotation(self.ActorNameMap[Name].StartRot);
self.ActorNameMap[Name].CurrPos = VectorHelper.ToLuaTable(self.ActorNameMap[Name].StartPos)
self.ActorNameMap[Name].CurrRot = VectorHelper.RotToLuaTable(self.ActorNameMap[Name].StartRot)
if self:HasAuthority() then
self:ServerSendRPC("ResetActor", InActor);
end
end
---@param InDeltaTime float
function MovingActorsManager:MoveAllActors1(InDeltaTime)
--UGCLogSystem.Log("[MovingActorsManager:MoveAllActors1] self.EnableMoving = %s, self.IsAlready = %s", tostring(self.EnableMoving), tostring(self.IsAlready));
if not self.EnableMoving then return end
if not self.IsAlready then return end
local CurrTime = KismetSystemLibrary.GetGameTimeInSeconds(self.Owner);
local NeedRemoveActors = {};
--UGCLogSystem.LogTree(string.format("[MovingActorsManager:MoveAllActors1] self.ActorNameMap ="), self.ActorNameMap)
for Name, Info in pairs(self.ActorNameMap) do
local Actor = self.ActorMap[Name];
if not UE.IsValid(Actor) then
NeedRemoveActors[#NeedRemoveActors + 1] = Name;
else
if Info.Func ~= nil and type(Info.Func) == 'function' then
local IsMove, MoveDir, MoveRotator = Info.Func(Actor, InDeltaTime);
if MoveRotator ~= nil and (MoveRotator.Pitch ~= nil or MoveRotator.Roll ~= nil or MoveRotator.Yaw ~= nil) then
-- 说明有这个
Info.Rotation = MoveRotator;
end
if MoveDir ~= nil then
-- 判断当前是什么
if MoveDir.X ~= nil or MoveDir.Y ~= nil or MoveDir.Z ~= nil then
Info.MoveDirection = VectorHelper.HandleLessVector(MoveDir);
end
if MoveDir.Pitch ~= nil or MoveDir.Yaw ~= nil or MoveDir.Roll ~= nil then
Info.Rotation = VectorHelper.HandleLessRotator(MoveDir);
end
end
Info.IsMove = IsMove;
else
-- 此时 Func 不存在,检查是否存在 MoveDirection
if Info.MoveDirection == nil and Info.Rotation == nil then
Info.IsMove = false;
end
end
if Info.IsMove then
if not table.isEmpty(Info.MoveDirection) then
local Delta = VectorHelper.ToLuaTable(VectorHelper.MulNumber(VectorHelper.HandleLessVector(Info.MoveDirection), InDeltaTime));
--UGCLogSystem.LogTree("[MovingActorsManager:MoveAllActors1] Delta = ", Delta);
local CurrPos = VectorHelper.Add(Actor:K2_GetActorLocation(), Delta);
Actor:K2_SetActorLocation(CurrPos);
Info.CurrPos = VectorHelper.ToLuaTable(CurrPos);
end
if not table.isEmpty(Info.Rotation) then
local Delta = VectorHelper.RotMulNumber(VectorHelper.HandleLessRotator(Info.Rotation), InDeltaTime);
local Rot = VectorHelper.RotToLuaTable( Actor:K2_GetActorRotation());
local CurrRot = VectorHelper.RotAdd(Delta, Rot);
Actor:K2_SetActorRotation(CurrRot);
Info.CurrRot = VectorHelper.RotToLuaTable(CurrRot);
end
end
end
end
end
function MovingActorsManager:ServerSendRPC(FuncName, ...)
UGCLogSystem.Log("[MovingActorsManager:ServerSendRPC] 执行函数:%s", FuncName);
self.Owner:SendMovingActorsManagerRPC(FuncName, ...);
end
function MovingActorsManager:ClientSendRPC(FuncName, ...)
UGCLogSystem.Log("[MovingActorsManager:ClientSendRPC] FuncName = %s", FuncName)
self.Owner:ClientMovingActorManagerSendRPC(FuncName, ...)
end
function MovingActorsManager:HandleCopy(InTable) return TableHelper.DeepCopy(self.ActorNameMap, "function"); end
--- 得到延迟(单位秒)
---@return float
function MovingActorsManager:GetPing()
local PC = UGCSystemLibrary.GetLocalPlayerController();
if UE.IsValid(PC) then return PC:GetPlayerNetworkPing() / 1000.; end
return 0.;
end
---@return bool 是否是服务器
function MovingActorsManager:HasAuthority() return UGCGameSystem.IsServer(); end
return MovingActorsManager;