What do you want to achieve?
I want a clone of the Player’s Character right before they leave.
What is the issue?
game.Players.PlayerRemoving is not fast enough.
The Player’s Character is instantly removed as soon as they leave.
There’s no real way to tell when a Player’s going to leave either.
What solutions have you tried so far?
I’ve tried just having a loop that saves a clone of the character’s body to a global variable with _G, but this might not be the best way to do it.
I am not trying to get a Roblox character.
My game does not use Roblox player characters for much of anything.
I am trying to get the Player object’s Character.
Ex:
Script 1
while wait() do
_G.[Player.UserId..'Char'] = Player.Character:Clone()
end
Script 2:
game.Players.PlayerRemoving:connect(function(Player)
local Char = _G.[Player.UserId..'Char']
Char.Parent = workspace
-- do stuff with the char
end)
I think the better option is to clone the character when the player joins and save the model in a a storage service., After the player leave you could parent the model to the workspace.
Have one script, preferably somewhere in ServerScriptService containing something along these lines:
game.Players.PlayerAdded:Connect(function(player)
(player.Character or player.CharacterAdded:Wait()):Clone().Parent = game.ServerStorage.Characters; --copies the character into game.ServerStorage.Characters
end);
game.Players.PlayerRemoving:Connect(function(player)
local carcass = game.ServerStorage.Characters:FindFirstChild(player.Name);
carcass.Parent = game.Workspace;
... --Do stuff here
end);
function cloneBody(character)
if game.ServerStorage.Characters:FindFirstChild(character.Name) then
game.ServerStorage.Characters[character.Name]:Destroy();
end
character:Clone().Parent = game.ServerStorage.Characters;
end
script.BindableEvent.Event:Connect(cloneBody); --BindableEvent parented to the script, pass in the character as an argument
game.Players.PlayerAdded:Connect(function(player)
cloneBody(player.Character or player.CharacterAdded:Wait());
end);
game.Players.PlayerRemoving:Connect(function(player)
local carcass = game.ServerStorage.Characters:FindFirstChild(player.Name);
carcass.Parent = game.Workspace;
... --Do stuff here
end);
You’d fire the BindableEvent whenever your game changes the appearance of the character.
In order to access all of those values after the player leaves, you will need to store them elsewhere. I would recommend managing a table in a serverscript that has an index for each player.
Agreed, storing values inside the Character is generally not a good idea. If having them managed by scripts is out of the question, then you should store them inside the Player.
I too would generally agree with you, however, I like to keep things simple.
In this game, you can morph between different bodies.
Each body you own has its own level.
If the player themselves were getting XP, I would definitely store the data onto a table that is referenced off the player, however, that would make things like trading bodies with other players complicated.
That code is still missing one thing that’s actually invisibly making your system fail.
Roblox player character models have their Archivable property set to false which prevents the copy method from being able to successfully copy. Make sure you set that to true before running copy, otherwise you might end up with nil returns from the copy operation.
Below I have a small repro that prints the result of a clone operation from two parts: one that has Archivable on and one that has it off.
Therefore in your code when you call char:Clone(), a nil value is actually being returned and you’re putting that into the charactersBeforeRemoving table. This means that the value at the index of the player’s UserId is nil, so the entry is nonexistent.