Best way to get Character of removing player?

  1. What do you want to achieve?
    I want a clone of the Player’s Character right before they leave.

  2. 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.

  3. 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.

MORE CONTEXT:
This is the game I’m working on:
https://www.roblox.com/games/3972064539/Everlands-2

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)

What’s the best way to do this?

3 Likes

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.

1 Like

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);

I guess I should probably point out that the Character’s body can change throughout the game.
Values inside the character’s body also change.

Then everytime a value is changed. clone the model, delete the previous one, and parent the new one into a stroage.

You could dynamically keep track of a HumanoidDescription for each player. This would make it easy to replicate the player character on demand.

1 Like

In that case, you could try this:

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.

I’m sorry, I’m not trying to get the Roblox character.
The ingame character is different than the Roblox Character.

The main things i’m trying to get are values inside the character.

1 Like

What kind of data are we talking about? Clothes and accessories? Or do you have some sort of inventory system tied to the character, etc

1 Like
  • The character’s HumanoidRootPart CFrame before (s)he left the game
  • The character’s Level and Exp (different than the Player’s level and Exp, it’s a roguelike game)
  • The seed used to create the character

You could use the Player.CharacterRemoving event to get the character right before it’s removed.

5 Likes

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.

1 Like

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.

Awesome, this works perfectly.

local charactersBeforeRemoving = {}

game.Players.PlayerAdded:connect(function(player)
	storage:LoadData(player.UserId)
	player.CharacterRemoving:connect(function(char)
		charactersBeforeRemoving[player.UserId] = char:Clone()
	end)
end)

game.Players.PlayerRemoving:connect(function(player)
	
	local removingChar = charactersBeforeRemoving[player.UserId]

-- stuff
3 Likes

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.

3 Likes

Thanks for the heads up!
Really appreciate it.
I’ve changed my code to do that.