399 lines
15 KiB
Lua
399 lines
15 KiB
Lua
---@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; |