I want to “record” a Humanoid players exact movement including jump for (arbitrary) 5 seconds, and then I want to move an NPC Humanoid in that exact path.
I tried saving the players humanoidrootpart position in a table every hearbeat and then loop through the table calling humanoid:MoveTo() on the NPC. But it takes much longer for the NPC to travel than the player.
I want the NPC to follow the same Humanoid physics. As in if the player goes through water the NPC will change HumanoidState to water.
If anyone has any ideas or any leads please let me know thanks.
Ideas:
Rather than :MoveTo perhaps use :Move() with a players humanoid.MoveDirection instead as :MoveTo function attempts to want to “stop and arrive perfectly at each position” hence lose momentum and be slower.
Additionally try using renderstep as it runs faster and could perhaps capture more user input but hearbeat seems ok as well.
I think your best shot at this is storing the players HumanoidRootPart CFrame inside of an array and iterating over that, setting the NPCs HumanoidRootPart CFrame. As for physics like the water state, I’m not sure. My best guess would be to just play the animations yourself as the NPC enters those states.
Pseudo-code? (ignore this, more so just me thinking out loud)
-- Client essentials
local player = game:GetService("Players").LocalPlayer
local character, humanoid, HRP
local function onCharacterAdded(newChar)
character = newChar
humanoid = character:WaitForChild("Humanoid")
HRP = character:WaitForChild("HumanoidRootPart")
end
if player.Character then
onCharacterAdded(player.Character)
end
player.CharacterAdded:Connect(onCharacterAdded)
-- Services
local RunService = game:GetService("RunService")
local UserInputService = game:GetService("UserInputService")
-- Variables
local NPC = workspace:WaitForChild("NPC")
local cframeArray = {}
local recording = false
local index = 0
-- Functions
local function playback(startIndex)
for i = startIndex, #cframeArray do
NPC.HumanoidRootPart.CFrame = cframeArray[i]
task.wait()
end
end
local function clearRecording()
table.clear(cframeArray)
index = 0
end
local function handleInputs(inputObject, typing)
if typing then return end
if inputObject.KeyCode == Enum.KeyCode.R and not recording then
recording = true
print("Recording CFrames :D")
elseif inputObject.KeyCode == Enum.KeyCode.R and recording then
recording = false
print("Stopped recording")
elseif inputObject.KeyCode == Enum.KeyCode.C and not recording then
clearRecording()
print("Reset recording")
elseif inputObject.KeyCode == Enum.KeyCode.P and not recording then
print("Playing recording")
playback(0)
end
end
-- Events
UserInputService.InputBegan:Connect(handleInputs)
RunService.Heartbeat:Connect(function(dt)
if recording then
cframeArray[index] = HRP.CFrame
index += 1
end
end)
Was messing around with the code above just for fun. It was actually my first time making something like it, and surprisingly it worked first try. All you’d really have to do here to ‘simulate’ an actual character is play animations during appropriate times. Assuming you wanted it to be local… and activated by pressing keys… yeah.
This code looks interesting. Just so I’m clear, this NPC will move at the same speed as the humanoid? And this is each achieved because you save a cframe each heartbeat, and use task.wait and task.wait waits a heartbeat?
I tried setting the CFrame, with the animations the HumanoidStateType automatically switches when it goes in water but it doesn’t automatically play the moving animation.
Update:
Updating the CFrame every heartbeat works. The only thing is it doesn’t respect physics, even though it’s following the exact path I want it to respect physics.
It’s because I am using this in a pathfinding system as a fallback when ComputeAsync() fails. But if the player jumps on a moving part lets say and then the NPC tries to jump on that part but it’s not there anymore I want the NPC to fall not to “jump on air”
Also I’m sure the animations are easy to add, but I never used any animations so can you explain how to do that more thanks
It’d be much better to record the method instead of recording the result.
As frames can fluctuate its impossible to get 100% accuracy. Probably something like 80% accurate movement. Really thats what we need.
You can store this data inside an array them play it back. Its very simple method and if you wanted you could include more actions driving a vehicle sitting in a chair, equipping a tool or jumping. I just included jumping for this one.
I made a quick script to demonstrate this.
local RunService = game:GetService("RunService")
local RECORD_TIME = 10
local Record = {-- We will be using a dictionary to record this
MoveDirection = {},
Jump = {}
}
-- Yeah its messy but whatever
local Player: Player = game.Players.PlayerAdded:Wait()
local Character = Player.CharacterAppearanceLoaded:Wait()
local Humanoid: Humanoid = Character:WaitForChild("Humanoid"):: Humanoid
task.wait(10) -- wait before recording
print("Recording")
local i = 1 -- iteration of recorder
local runTime = 0 -- RunTime of recorder
while (runTime < RECORD_TIME) do
Record.MoveDirection[i] = Humanoid.MoveDirection
Record.Jump[i] = Humanoid.Jump -- Save actions
i += 1
runTime += task.wait()
end
local HumanoidNPC = script.Parent.Humanoid
while i > 1 do -- Playback recorder
i -= 1
HumanoidNPC:Move(Record.MoveDirection[i])
HumanoidNPC.Jump = Record.Jump[i] -- play actions
task.wait()
end
No animations but…you get the idea. Note I did move a bit after the recording finished so thats why it didn’t align in the end.
Only problem is, it’s way too inaccurate. Put a few obstacles and try jumping over and around them and the Dummy completely misses everything.
So far the thing that worked is what @NoParameters said to update the cframe every heartbeat. The only problem is that it doesn’t respect physics.
How would I make it so that physics do apply?
I thought instead of moving the NPC to the exact CFrame the player traveled I would record the object space CFrame that the player is travelling and then offset it on the NPCs CFrame.
I tried that and it’s not working but I also don’t know how to use ToObjectSpace and ToWorldSpace properly.
The only feasible way you could do is to use AlignPosition and AlignOrientation to move the NPC to the target CFrame. They have the capability to do that. For it to work you have to make an attachment where the CFrame is parent it to the terrain and then move character that way(Don’t use Mode.OneAttachment cos it is broken). You could have this on top of the current action system if you wanted to but it isn’t necessary.
I have a question regarding this topic, is it also possible to replicate the position of the player’s arms/legs in order to make it seem more realistic?