How can I make my custom hats replicate smoothly?

In brevity, I have a custom avatar using skinned meshes and I’m updating the hat’s CFrame relative to a bone’s WorldTransformedCFrame so the hat bobs up and down with the character (due to the animation) rather than the head clipping through the hat.

Testing alone, this seems to work fine. With others, there poses a slight annoyance. I even opened a new place and basically wrote a dumbed down, but, similar system where a part is constantly updated to the player’s head. I set network ownership of the part to the client who’s updating it, tested and it worked fine. However, after about a minute or so, the hat then disappeared?

Here’s the simplified version of what I’m trying to do.

Server-side script creating the hat and setting network ownership...
local _gameServices = {
	replicatedStorage = game:GetService("ReplicatedStorage");
	
	players = game:GetService("Players")
}


local function OnPlayerAdded( player )
	
	repeat wait() until player.Character
	
	local function OnCharacterAdded( character )
		local hat = _gameServices.replicatedStorage.Hat:Clone()
		hat.BrickColor = BrickColor.Random()
		hat.Parent = character
		
		hat:SetNetworkOwner(player)
		
		_gameServices.replicatedStorage.OnHatEquipped:FireAllClients(player, hat)
	end
	
	player.CharacterAdded:Connect(OnCharacterAdded)
	
	OnCharacterAdded(player.Character)
	
end


_gameServices.players.PlayerAdded:Connect(OnPlayerAdded)


for _, player in next, _gameServices.players:GetPlayers() do
	OnPlayerAdded(player)
end

Client-side script updating the hat's CFrame every render step...
local _gameServices = {
	replicatedStorage = game:GetService("ReplicatedStorage");
	
	runService = game:GetService("RunService");
}


local players = {}


local function renderHat( player, hat )
	
	hat.CanCollide = false
	
	if (players[player]) then
		players[player]:Disconnect()
		players[player] = nil
	end
	
	players[player] = _gameServices.runService.RenderStepped:Connect(function()
		hat.CFrame = player.Character.Head.CFrame
	end)
	
end


local onHatEquipped = _gameServices.replicatedStorage.OnHatEquipped
onHatEquipped.OnClientEvent:Connect(function( player, hat )
	renderHat(player, hat)
end)

The strangest issue I’m having is how it seems to disappear / destroy itself after a short while. I’m sorry if this is lacking detail or evidence, I’ll be happy to try and provide whatever’s necessary.

Cheers! (:

I get this problem a lot, because the hat is unanchored, it will continually gain downward velocity from gravity
you can stop this by setting the velocity to Vector3.new(0,0,0) in Renderstepped function

also, it’s preferred that you use RunService.Stepped or .Heartbeat because those are more physics oriented and you should avoid binding to .Renderstepped
Info on Binding to RunService stuff

1 Like

Ah! That makes a lot of sense, thank you very much!
There is a slight bit of jitteriness, even in an empty game (could this just be my computer’s handling?) could I substitute setting the CFrame directly to interpolating, to try and smooth it out without affecting performance too much?

how does the jittering look? I don’t think there should be jittering since you’re doing this every frame