I’m trying to make a system that create a clone and it replay your previous life movement and action
This is the video, you can see some npcs just kinda lagged mid air and some teleported at some point.
my npc local script
local player = game:GetService('Players').LocalPlayer
local RunService = game:GetService('RunService')
local ContextActionService = game:GetService('ContextActionService')
local uis = game:GetService("UserInputService")
local jumping = false
local leftValue, rightValue = 0, 0
local function tick()
return DateTime.now().UnixTimestampMillis / 1000
end
local startTime = tick()
local cam = workspace.CurrentCamera
local char = player.Character or player.CharacterAdded:Wait()
local hum = char:WaitForChild("Humanoid")
local animator = hum:WaitForChild("Animator")
local hrp = char:WaitForChild("HumanoidRootPart")
local rs = game:GetService("ReplicatedStorage")
local animations = rs:WaitForChild("Animations")
local remote = rs.Remote
local turnRemote = remote.Turn
local animationPack = animations:WaitForChild("Private")
local idleLeft = animationPack:WaitForChild("IdleLeft")
local idleTrackLeft = animator:LoadAnimation(idleLeft)
local idleRight = animationPack:WaitForChild("IdleRight")
local idleTrackRight = animator:LoadAnimation(idleRight)
idleTrackLeft:Play()
local side = ""
char:SetAttribute("Side", "D")
local data = {}
local holdingA = false
local holdingD = false
local holdingM1 = false
local holdingJump = false
local state = "Idle"
local function onLeft(actionName, inputState)
if inputState == Enum.UserInputState.Begin then
if side == "D" or side == "" then
idleTrackLeft:Stop()
idleTrackRight:Play()
hrp.CFrame = CFrame.new(hrp.CFrame.Position) * CFrame.Angles(0,math.pi*.5,0)
side = "A"
char:SetAttribute("Side", "A")
end
holdingA = true
leftValue = 1
elseif inputState == Enum.UserInputState.End then
leftValue = 0
holdingA = false
end
end
local function onRight(actionName, inputState)
if inputState == Enum.UserInputState.Begin then
if side == "A" or side == "" then
idleTrackRight:Stop()
idleTrackLeft:Play()
hrp.CFrame = CFrame.new(hrp.CFrame.Position) * CFrame.Angles(0,-math.pi*.5,0)
side = "D"
char:SetAttribute("Side", "D")
end
holdingD = true
rightValue = 1
elseif inputState == Enum.UserInputState.End then
rightValue = 0
holdingD = false
end
end
local function onJump(actionName, inputState)
if inputState == Enum.UserInputState.Begin then
--table.insert(data,{"Action";tick() - startTime; "dW"})
jumping = true
elseif inputState == Enum.UserInputState.End then
--table.insert(data,{"Action";tick() - startTime; "uW"})
jumping = false
end
end
local function onUpdate()
if player.Character and player.Character:FindFirstChild('Humanoid') then
if jumping then
player.Character.Humanoid.Jump = true
end
local moveDirection = rightValue - leftValue
player.Character.Humanoid:Move(Vector3.new(moveDirection,0,0), false)
end
end
RunService:BindToRenderStep('Control', Enum.RenderPriority.Input.Value, onUpdate)
ContextActionService:BindAction('Left', onLeft, true, 'a', Enum.KeyCode.A, Enum.KeyCode.DPadLeft)
ContextActionService:BindAction('Right', onRight, true, 'd', Enum.KeyCode.D, Enum.KeyCode.DPadRight)
ContextActionService:BindAction('Jump', onJump, true, 'w', Enum.KeyCode.W, Enum.KeyCode.ButtonA)
game:GetService("RunService").RenderStepped:Connect(function()
cam.CameraType = Enum.CameraType.Scriptable
cam.CFrame = cam.CFrame:Lerp(CFrame.new(hrp.Position + Vector3.new(0, 5, 30), hrp.Position), 0.05)
end)
uis.InputBegan:Connect(function(input, isTyping)
if isTyping then return end
if input.UserInputType == Enum.UserInputType.MouseButton1 then
table.insert(data,{"Action";tick() - startTime; "dM1"})
end
end)
uis.InputEnded:Connect(function(input)
if input.UserInputType == Enum.UserInputType.MouseButton1 then
table.insert(data,{"Action";tick() - startTime; "uM1"})
end
end)
hum.Died:Connect(function()
print(data)
remote:WaitForChild("SendData"):FireServer(data)
end)
local startPos = hrp.Position
hum.Jumping:Connect(function(IsJumping)
if IsJumping then
table.insert(data,{"Jump";hrp.Position;tick() - startTime})
end
end)
local moveWait
RunService.RenderStepped:Connect(function()
for i,v in pairs(workspace:WaitForChild("Platforms"):GetChildren()) do
if v.Position.Y < hrp.Position.Y-2.5 then
v.CanCollide = true
else
v.CanCollide = false
end
end
--print(tostring(holdingA).."--"..tostring(holdingD))
if holdingA and not holdingD then
if state == "MovingRight" then
table.insert(data, {"Move";hrp.Position;"Right";0})
elseif state == "Idle" then
moveWait = tick() - startTime
end
state = "MovingLeft"
--print("MovingLeft")
elseif holdingD and not holdingA then
if state == "MovingLeft" then
table.insert(data, {"Move";hrp.Position;"Left";0})
elseif state == "Idle" then
moveWait = tick() - startTime
end
state = "MovingRight"
--print("MovingRight")
end
if holdingA == false and holdingD == false and state ~= "Idle" then
if state == "MovingRight" then
table.insert(data, {"Move";hrp.Position;"Right";moveWait})
elseif state == "MovingLeft" then
table.insert(data, {"Move";hrp.Position;"Left";moveWait})
end
state = "Idle"
--print("idle")
end
end)
my npc server code
local CD = 0.5
for i,v in pairs(script.Parent:GetDescendants()) do
if v:IsA("Part") or v:IsA("MeshPart") then
v:SetNetworkOwner(nil)
end
end
local rs = game:GetService("ReplicatedStorage")
local Queue = require(rs.Module:WaitForChild("Queue"))
local npcFold = script.Parent
local cloneNumber = npcFold.CloneNumber.Value
local char = npcFold.Parent
local myTeam = char.Parent
local hum = char.Humanoid
local hrp = char.HumanoidRootPart
local animator = hum.Animator
char.PrivateServerScript.Enabled = false
char["2DMovement"].Enabled = false
char.CharacterScript.Enabled = false
local cloneData
if myTeam == workspace.Team1 then
cloneData = _G.Team1CloneData[cloneNumber]
else
cloneData = _G.Team2CloneData[cloneNumber]
end
local RunService = game:GetService("RunService")
local ss = game:GetService("ServerStorage")
local module = rs.Module
local fastCast = require(module.FastCastRedux)
local models = ss.Models
local remote = rs.Remote
local animation = rs.Animations.Private
local bullet = models.Bullet
local hum = char.Humanoid
local hrp = char.HumanoidRootPart
local animator = hum.Animator
local caster = fastCast.new()
local behavior = fastCast.newBehavior()
local params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Exclude
params.FilterDescendantsInstances = {char; workspace.Platforms; script.Parent.Parent.Parent}
behavior.RaycastParams = params
behavior.Acceleration = Vector3.new(0,0,0)
behavior.AutoIgnoreContainer = true
behavior.CosmeticBulletTemplate = bullet
behavior.CosmeticBulletContainer = workspace.Projectiles
behavior.MaxDistance = 35
caster.LengthChanged:Connect(function(ActiveCast, lastPoint, rayDir, displacement, segmentVelocity, cosmeticBulletObject)
local newPos = lastPoint + (rayDir * displacement)
cosmeticBulletObject.Position = newPos
end)
caster.RayHit:Connect(function(ActiveCast, RaycastResult, segmentVelocity, cosmeticBulletObject)
--print("hit "..RaycastResult.Position.X)
cosmeticBulletObject:Destroy()
end)
caster.CastTerminating:Connect(function(ActiveCast, a)
local boolet = ActiveCast.RayInfo.CosmeticBulletObject
boolet:Destroy()
end)
function shoot(side, pos)
local dir
if side == "A" then
dir = Vector3.new(-50,0,0).Unit
else
dir = Vector3.new(50,0,0).Unit
end
caster:Fire(pos, dir, 40, behavior)
end
local holdingA = false
local holdingD = false
local holdingM1 = false
local holdingJump = false
local speed = hum.WalkSpeed
local walkTrack = animator:LoadAnimation(animation.Parent.WalkAnim)
local idleRightTrack = animator:LoadAnimation(animation.IdleRight)
local idleLeftTrack = animator:LoadAnimation(animation.IdleLeft)
local canMove = true
local turnPart = Instance.new("Part", workspace)
turnPart.Transparency = 1
turnPart.CanTouch = false
turnPart.CanCollide = false
turnPart.CanQuery = false
turnPart.Anchored = true
turnPart.CFrame = turnPart.CFrame * CFrame.Angles(0,- math.pi / 2,0)
local att1 = Instance.new("Attachment", turnPart)
hrp.AlignOrientation.Attachment1 = att1
-----------------------------------------------MOVEMENT
function moveToPoint(pos, dir)
canMove = false
if dir == "Left" then--hrp.Position.X >= pos.X then
local moveCF = CFrame.new(hrp.CFrame.Position) * CFrame.Angles(0,math.pi*.5,0)
local lookat = CFrame.lookAt(hrp.Position, Vector3.new(hrp.Position.X-10, hrp.Position.Y, 0))
turnPart.CFrame = lookat
char:SetAttribute("Side","A")
hum:Move(moveCF.LookVector)
idleLeftTrack:Stop()
idleRightTrack:Play()
task.spawn(function()
local endL = false
while task.wait() and not endL do
if hrp.Position.X <= pos.X then
hum:Move(Vector3.new(0,0,0))
canMove = true
endL = true
end
end
end)
elseif dir == "Right" then --hrp.Position.X < pos.X then
local moveCF = CFrame.new(hrp.CFrame.Position) * CFrame.Angles(0,-math.pi*.5,0)
local lookat = CFrame.lookAt(hrp.Position, Vector3.new(hrp.Position.X+10, hrp.Position.Y, 0))
turnPart.CFrame = lookat
char:SetAttribute("Side","D")
hum:Move(moveCF.LookVector)
idleLeftTrack:Play()
idleRightTrack:Stop()
task.spawn(function()
local endL = false
while task.wait() and not endL do
if hrp.Position.X >= pos.X then
hum:Move(Vector3.new(0,0,0))
canMove = true
endL = true
end
end
end)
end
end
local startTime = tick()
task.spawn(function()
for i,v in pairs(cloneData) do
--print(cloneData[i][2])
if cloneData[i][1] == "Move" then
while canMove == false do
task.wait()
end
task.wait(cloneData[i][4] + startTime - tick())
moveToPoint(cloneData[i][2],cloneData[i][3])
--print("move")
end
if cloneData[i][1] == "Action" then
if cloneData[i][3] == "dM1" then
task.spawn(function()
task.wait(cloneData[i][2] + startTime - tick())
holdingM1 = true
end)
end
if cloneData[i][3] == "uM1" then
task.spawn(function()
task.wait(cloneData[i][2] + startTime - tick())
holdingM1 = false
end)
end
end
end
end)
-----------------------------------JUMPING
task.spawn(function()
for i,v in pairs(cloneData) do
if cloneData[i][1] == "Jump" then
task.wait(cloneData[i][3] + startTime - tick())
local jumped = false
local endLoop = false
while task.wait() and not endLoop do
hum.Jump = true
jumped = true
hum.Jumping:Connect(function(IsJumping)
if IsJumping then
endLoop = true
end
end)
end
end
end
end)
local lookat = CFrame.lookAt(hrp.Position, Vector3.new(10, hrp.Position.Y, 0))
hrp.CFrame = lookat
local currTime = tick()
while task.wait() do
if holdingM1 then
if currTime + CD <= tick() then
currTime = tick()
if char:GetAttribute("Side") == "D" then
local shootLeftTrack = animator:LoadAnimation(animation:WaitForChild("ShootLeft"))
shootLeftTrack:Play()
shootLeftTrack:GetMarkerReachedSignal("Shoot"):Connect(function()
shoot(char:GetAttribute("Side"), hrp.Position + Vector3.new(0,1,0))
end)
else
local shootRightTrack = animator:LoadAnimation(animation:WaitForChild("ShootRight"))
shootRightTrack:Play()
shootRightTrack:GetMarkerReachedSignal("Shoot"):Connect(function()
shoot(char:GetAttribute("Side"), hrp.Position+ Vector3.new(0,1,0))
end)
end
end
end
end
Some explanation for my scripts:
-
In local i record player position every time they turn and also the waiting time if the player stand still.
-
I also record the position and time every time they jump.
-
After player die, i send the recorded data to the server and store it in a global variable.
-
In server script i run 2 while loop together using task.spawn, 1 for looping through the movement and 1 looping through jumping. Ignore the shooting bc im revamping it soon.
-
The problem is not server latency bc it lagged on the server perspective too