Hello Developers,
For my parkour game I will create a ghost effect. When a Player beat a specific Level, throughout the whole level the keyframes of the Players Torso will be recorded. After the Player beat the Level the keyframes will be saved into a folder. These keyframes will be replayed every time the player plays the Level again. For example the keyframes could play if the player clicked the “start” button of a level.
-Also these keyframes should be updated every time the Player reached a new high score (better time than before), but that’s not too important, just an little extra feature
I would only save the keyframes of the players torso since saving the whole (R6!) body would just create lag issues when replaying the “ghost” effect.
If anyone knows how to implement this “saving of keyframes of a specific body part of a Player and replaying it when called” function, feel free to show me how its done
Looking forward to a reply!
Hey there,
I was thinking about that, but wasn’t sure if there were any better methods. And I am not sure how to save the CFrame of the torso of the Player as a value, may you help me with this?
One Idea I have (to prevent lag) is to only save the Frame every 5 seconds. I will use an arrow as a Pointer were you have been at the time you are now. The arrow will smoothly tween to the points which are saved. This would be a very cheap method
The coolest feature would be to save the whole players character (so all body parts CFrames), but I am not sure how to implement this without massive lag, since the maps can get to 5 minutes long.
If its only every 5 seconds then you should have no problem with lag even with all 6 body parts.
To save the values you can put them in a table in the script. In a local script under the character you can do something like this:
local RunService = game:GetService('RunService')
local character = script.Parent
local parts = {
character.Torso,
character.Head,
character.LeftLeg,
character.RightLeg,
character.LeftArm,
character.RightArm
}
local cfTables = {
['Torso'] = {},
['Head'] = {},
['LeftLeg'] = {},
['RightLeg'] = {},
['LeftArm'] = {},
['RightArm'] = {}
}
local t = 0
RunService.Heartbeat:Connect(function(dt)
--only run every 5 seconds
t += dt
if t < 5 then return end
t = 0
--
for _,v in ipairs(parts) do
table.insert(cfTables[v.Name], v.CFrame)
end
end)
this code will save the value of all limbs cframes in the cfTables table every 5 seconds.
i can also help with things like replaying it, updating it to faster time, etc. if you need.
Thank you so much for this, that’s amazing! I will try it out with a Datastore. Also it would be an INSANE help if you could tell me how to replay the keyframes. Replacing the Table with a better time is done already (basically if the time you got is faster then the previous the table gets restored with the new keyframes).
To replay the ghost you will just set the ghost’s body parts cframes to the appropriate values every 5 seconds. You can use the same heartbeat connection to play the ghost as you can to save the cframes
local RunService = game:GetService('RunService')
local character = script.Parent
local ghost = --insert ghost model here
local parts = {
character.Torso,
character.Head,
character.LeftLeg,
character.RightLeg,
character.LeftArm,
character.RightArm
}
local ghostParts = {
ghost.Torso,
ghost.Head,
ghost.LeftLeg,
ghost.RightLeg,
ghost.LeftArm,
ghost.RightArm
}
local cfTablesNew = {
['Torso'] = {},
['Head'] = {},
['LeftLeg'] = {},
['RightLeg'] = {},
['LeftArm'] = {},
['RightArm'] = {}
}
local cfTablesCurrent = {
['Torso'] = {},
['Head'] = {},
['LeftLeg'] = {},
['RightLeg'] = {},
['LeftArm'] = {},
['RightArm'] = {}
}
local t = 0
local count = 0
RunService.Heartbeat:Connect(function(dt)
--only run every 5 seconds
t += dt
if t < 5 then return end
t = 0
--
count += 1
for _,v in ipairs(parts) do
table.insert(cfTablesNew[v.Name], v.CFrame)
end
for _,v in ipairs(ghostParts) do
v.CFrame = cfTablesCurrent[v.Name][count]
end
end)
(local script will mean the ghost is only visible to that one player but if you want different it can be done)
so as written this code would not exactly work, you need implement your way to update the cfTablesCurrent (this is the current record), and you need to make this heartbeat function only start working once the stage has started (and stop when its ended).
But anyway the idea is that at the same time the players current cframes are recorded to the new table cfTablesNew, the ghost’s cframes are set to the old values from cfTablesCurrent. the count variable keeps track of which cframe needs to be loaded so you need to reset that to 0 once you start a new stage. You also should anchor the ghost so it stays where its set.
if newtime < oldtime then cfTablesCurrent = cfTablesNew end
this will actually just make cfTablesCurrent reference cfTablesNew instead of copying all the values, and so any change you do to the cfTablesNew will actually change what you have stored as the old record. maybe you have done it a different way though. just a heads up
if youre storing the old record in a datastore like you said then thats fine. you just need the old record stored seperate from the record being currently recorded, so that you can decide whether or not to keep the new one if it is faster or not (you dont want to destroy the old record and then the new record is actually slower)
AHHH
Ive been trying for so long now and the “Ghost” just sticks to me like its in creative mode lol.
I think I made something wrong, also it doesn’t seems like the “Ghost’s” parts have the same orientation of me. I will try to fix everything and try to “start recording” as soon as the player starts a level, not every time