I’m currently working on a game where players can customise their house interior (which is rendered on the client), but I want to make it so that other players are hidden if they are not in the same house/not in a house and vice versa. I don’t want to have to use iterate through a character’s BasePart to make it transparent as other BaseParts are added in and out and would just require a lot of client processing.
So instead I thought of a “solution” where a client would parent other characters to the ReplicatedStorage to hide them, and then back into workspace when they need to be visible. However, I came across a replication issue where the other character’s position wouldn’t update until they move. See video below:
Is there any way to update their current position when they become visible? I understand that this occurs due to the network ownership etc.
Is there any other solution to making other characters invisible? Unless iterating through their BaseParts is the only solution that updates their position when they are visible.
The easiest way is to iterate through the characters you want to blacklist and set their transparency (ik you don’t want to do that but it’s the easiest way with the least amount of overhead) then add a ChildAdded connection and if a basepart is added onto a player, set its transparency. Then just disconnect the function and set the transparency back to normal when you’re done editing the house
On the client, parent all other characters to some folder in ReplicatedStorage when you want to unrender them, then take them back out when you want them re-rendered.
Of course. Just anchor them on the client and move them to an unseeable position. Or, you could possibly parent them to nil instead of ReplicatedStorage (though this would probably do the same thing).
I’d assume this is a bug, so I’d recommend creating a bug report.
I tried anchoring them too, it’s the exact same issue but rather instead of becoming invisible, they’re in place. Unanchoring will keep them in place until the other player moves (in the same way in the video).
Have a folder in workspace for storing characters
This does not account for rotation, which can be fixed by storing character rotations with either another attribute or string manip to keep all info in 1 attribute value
-- game/ServerScriptService/
-- parents loaded characters into a folder; not necessary, but i use this in every project
local Players = game:GetService("Players")
Players.PlayerAdded:Connect(function(Player : Player)
Player.CharacterAdded:Connect(function(Character : Model)
repeat task.wait() until Character:IsDescendantOf(workspace)
Character.Parent = workspace.Living
Character.PrimaryPart = Character:WaitForChild("HumanoidRootPart")
end)
end)
-- game/ServerScriptService/
-- saves last position of living characters at intervals of RefreshRate
local RunService = game:GetService("RunService")
local CharacterFolder = workspace.Living
local RefreshRate = 0.2
local t = 0
RunService.Heartbeat:Connect(function(dt: number)
t += dt
if t < RefreshRate then return end
t = RefreshRate - t
for _, Character in CharacterFolder:GetChildren() do
local Humanoid = Character.Humanoid
local RootPart = Character.HumanoidRootPart
if Humanoid.Health == 0 then continue end
Character:SetAttribute("LastPosition", RootPart.Position)
end
end)
-- LocalScript
-- game/StarterPlayer/StarterCharacterScripts/
-- listens for ChildAdded, then after some sanity checks it will correct last known position
local CharacterFolder = workspace:WaitForChild("Living")
CharacterFolder.ChildAdded:Connect(function(Character: Model)
local LastPosition = Character:GetAttribute("LastPosition")
if not LastPosition then return end
local Humanoid = Character.Humanoid
local MoveDirection = Humanoid.MoveDirection
if MoveDirection.Magnitude ~= 0 then return end
Character:MoveTo(LastPosition)
end)
what if when you put other players back into the workspace from replicatedstorage, you also ask the server for their position? a remotefunction maybe, the server returns their position and orientation, and the client takes that information and places characters as they should be.
As a shorterm solution to the parenting method, you could just have a remote event communicate to the server so that it gets the players character model’s Pivot, so from the local script you could just
remote.OnClientEvent:Connect(function(PivotsTable:{{plr_name:CFrame}})
for plrname,Cf:CFrame in pairs(PivotsTable) do
local char:Model = workspace:FindFirstChild(plrname)
if char ~= nil then char:PivotTo(Cf) end
end
end)
But i don’t know if that would be actually better than just setting the transparency to nil, still have to iterate for each of the models.
Yes, I did read your post, I have tried it before posting (them being in the sky is just a temporary hidden area), regardless if you Pivot them back to the original spot or not, it is still required that they move in order to update. Still the same issue.
Clean and short solution, just wish there was a solution that doesn’t require the server to run Heartbeat for performance reasons as there will be 30 player servers (or maybe this isn’t even an issue). Thanks for this!
You can change the method of updating positions to a while loop, but given how they immediately stop looping after an error I personally don’t use them; the way I see it is sacrificing slight performance for longevity, knowing that functions bound to heartbeat will continue to run even after an error is thrown
This is also why I added the interval, so that the real function only runs every x seconds instead of every frame
Perhaps I can use your method but instead of doing loop, maybe just update accordingly when players leave and enter houses. Thanks a lot for the assistance.
Replicated storage is a snapshot of when the data/information was saved. It will not update until you save new data/information to it. I think there are better ways of doing this, but I would suggest that you do not rely on replication to do it for you!
If you want to make other players characters appear in their actual location when returning to workspace you can recall their actual position/CFrame from the server using a RemoteFunction, and on the Client side move them using :PivotTo() or :SetPrimaryPartCFrame() to the CFrame received from the server so that characters are at least approximately where they are currently located on his behalf.
This is just my guess, because I have not tested this in practice, but it will still be good if this method turns out to be correct. I think a couple of requests in RemoteFunction won’t worsen the server performance.
Client:
local YourRemoteFunc = insertrequestedhere
local TargetFolder = inserthiddenplayersfolder
YourRemoteFunc.OnClientInvoke = function(targetPlr,hide)
local char
local targParent = workspace
if hide == true then
char = targetPlr.Character
targParent = TargetFolder
elseif hide == false then
char = TargetFolder:FindFirstChild(targetPlr.Name)
end
if char then
char.Parent = targParent
if hide == false then
local targetCF = YourRemoteFunc:InvokeServer(targetPlr)
if targetCF then
char:SetPrimaryPartCFrame(targetCF)
end
end
end
end
Server:
local YourRemoteFunc = insertrequestedhere
YourRemoteFunc.OnServerInvoke = function(targetPlr,hide)
local char = targetPlr.Character
local hrp = char:FindFirstChild("HumanoidRootPart")
if char and hrp then
local primary = char.PrimaryPart
if primary then
return primary.CFrame
end
end
end