Username script

So here’s my username code:

local RunService = game:GetService("RunService")
local baseName = "Draw Name "
local registered = {}
local function unregister(name)
	if typeof(name) == "Instance" then name = name.Name end
	RunService:UnbindFromRenderStep(baseName..name)
	if registered[name] then registered[name]:Destroy() end
end
function register(player:Player)
	if player == game.Players.LocalPlayer then return end
	local label = script.Username:Clone()
	label.Text.Text = player.Name
	label.Name = player.Name
	label.Text.TextColor = player.TeamColor
	repeat wait() until player.Character
	local char = player.Character
	local name = player.Name
	if registered[name] then unregister(name) end
	label.Parent = game.Players.LocalPlayer.PlayerGui
	label.Adornee = char:WaitForChild("Head")
	local debounce = false
	registered[name] = label
	RunService:BindToRenderStep(baseName..name, Enum.RenderPriority.Camera.Value - 1, function()
		local camera = workspace.CurrentCamera
		if not char or not char:FindFirstChild("Head") or not label or not label:FindFirstChild("Text") then
			warn("Sanity check failed", char, char:FindFirstChild("Head"), label, label:FindFirstChild("Text"))
			unregister(name)
			return
		end
		local distance = (camera.CFrame.Position-char.Head.Position).Magnitude
		local size = 23-(5*math.clamp(math.floor(distance/25), 0, 3))
		if distance > 90 then
			local transparency = math.clamp((distance-90)/10, 0, 1)
			label.Text.TextTransparency = transparency
		end
		label.Size = UDim2.new(label.Size.X.Scale, label.Size.X.Offset, 0, size)
	end)
end
function playerAdded(player)
	if player == game.Players.LocalPlayer then return end
	if player.Character then register(player) end
	player.CharacterAdded:Connect(function()
		register(player)
	end)
	player.CharacterRemoving:Connect(function()
		unregister(player)
	end)
	player:GetPropertyChangedSignal("TeamColor"):Connect(function()
		local label = registered[player.Name]
		if label and label:FindFirstChild("Text") then
			label.Text.TextColor = player.TeamColor
		end
	end)
end
repeat task.wait() until game:IsLoaded()
for i,player in pairs(game.Players:GetPlayers()) do
	playerAdded(player)
end
game.Players.LocalPlayer.CharacterAdded:Connect(function() -- Fixes some weirdness
	for i,player in pairs(game.Players:GetPlayers()) do
		unregister(player)
		task.wait()
		register(player)
	end
end)
game.Players.PlayerAdded:Connect(playerAdded)

And a model with a premade billboard: https://create.roblox.com/marketplace/asset/15274947357/Custom-username-with-scaling

The goal of the script is to replicate how default usernames work where they shrink in a step-wise pattern the further you get + the fading
I can’t help but feel that this code is probably a bit flawed. Please give me ideas for improvements.

1 Like

A lot of things to improve:

1. Reduce Loops

Loops tank performances as it uses a lot of CPU power. It stays on the tasks/frame until it is completed. It is actively using the CPU and memory.

Read more:
Events | Roblox Lua Programming Formalities Guide - Resources / Community Resources - Developer Forum | Roblox

Loop 1

--try
if not game:IsLoaded() then
     game.Loaded:wait()
end
--instead of
repeat task.wait() until game:IsLoaded()

Read more:
DataModel | Documentation - Roblox Creator Hub
Events | Roblox Lua Programming Formalities Guide - Resources / Community Resources - Developer Forum | Roblox

Loop 2

Why are looping until something is true? When you can use :Wait() function.

-- Use
if not player.Character then
    player.CharacterAdded:Wait()
end

-- Instead of
repeat wait() until player.Character

Read more:
Player | Documentation - Roblox Creator Hub
Events - Roblox Lua Programming Formalities Guide - Resources / Community Resources - Developer Forum | Roblox

2. Define all services at the top

local Players = game:GetService("Players")
local RunService = Game:GetService("RunService")
-- etc

Once you define it, use that assignment.
Read more:
Services | Documentation - Roblox Creator Hub
Services | Roblox Lua Programming Formalities Guide - Resources / Community Resources - Developer Forum | Roblox

3. Don’t use wait()

It is not efficient, and can cause performance issues. Use task.wait()

-- Use
task.wait(1)

-- Instead of
wait(1)

Read more: task | Documentation - Roblox Creator Hub

4. Is your script overcomplexed?

Why are you using? RunService:BindToRenderStep() Why is the local client worrying about another client (or other clients)? It looks like you are trying to create a nametag program, that is doing a math calculation, this eating up the performance of the client.

Here is what I would do:
This was programmed for the client! :smiley:

--!strict
-- Global Assignments / Constants (PascelCase) # Assignments that doesn't change
local Players = game:GetService("Players")

-- Local Assignments (camalCase) # Assignments that will (or going to change)
local labelTemplate: BillboardGui | TextLabel | {["Text"]: TextLabel, ["Clone"]: any, ["MaxDistance"]: number} = script:WaitForChild("Username")-- :: BillboardGui

local connections = {}
function createNameTag(player: Player) : boolean
	connections[player.UserId] = {}
	local character: Model = player.Character or player.CharacterAdded:Wait()
	local label = labelTemplate:Clone()
	label.Text.Text = player.Name
	label.Parent = character
	label.Adornee = character:WaitForChild("Head")
	label.Name = player.Name -- IDK why you want that, but cool
	label.Text.TextColor = player.TeamColor
	label.Text.TextScalable = true
	table.insert(connections[player.UserId], player:GetPropertyChangedSignal("TeamColor"):Connect(function()
		label.Text.TextColor = player.TeamColor
	end))
	
	table.insert(connections[player.UserId], player.CharacterAdded:Connect(function()
		label.Parent = character
		label.Adornee = character:WaitForChild("Head")
	end))
	
	return true
end

labelTemplate.MaxDistance = 90 -- Type error, cause Roblox said so... IDK why it mad

Players.PlayerRemoving:Connect(function(player: Player)
	for _,connection in connections[player.UserId] do
		if connection.Connected then
			connection:Disconnect()
		end
	end
	connections[player.UserId] = nil
end)

Players.PlayerAdded:Connect(createNameTag)

for _,player: Player in  Players:GetPlayers() do
	createNameTag(player)
end

Check out these posts to improve your programming skills

Roblox Lua Programming Formalities Guide - Resources / Community Resources - Developer Forum | Roblox
All about optimization - Resources / Community Tutorials - Developer Forum | Roblox
Type checking for beginners! - Resources / Community Tutorials - Developer Forum | Roblox

Sources

DataModel | Documentation - Roblox Creator Hub
Player | Documentation - Roblox Creator Hub
Events - Roblox Lua Programming Formalities Guide - Resources / Community Resources - Developer Forum | Roblox
Servuces - Roblox Lua Programming Formalities Guide - Resources / Community Resources - Developer Forum | Roblox
Services | Documentation - Roblox Creator Hub
task | Documentation - Roblox Creator Hub

1 Like

The first 3 suggestions are good, but the 4th throws out the entire point
The point of the script is to scale the name in a step-wise pattern similar to the default humanoid names + some fading when 90 studs or more away
I did some quick benchmarking + some minimizing and each iteration takes about 0.000015 seconds to run so it’s not really hurting performance
Anyway here’s my revised code (yes I did yoink your typechecked label thing for fun)

local RunService = game:GetService("RunService")
local Players = game.Players
local BaseName = "Draw Name "
local labelTemplate: BillboardGui | TextLabel | {["Text"]: TextLabel, ["Clone"]: any, ["MaxDistance"]: number} = script:WaitForChild("Username")
local registered = {}
local function unregister(name)
	if typeof(name) == "Instance" then name = name.Name end
	RunService:UnbindFromRenderStep(BaseName..name)
	if registered[name] then registered[name]:Destroy() end
end
function register(player:Player)
	if player == game.Players.LocalPlayer then return end
	local label = labelTemplate:Clone()
	label.Text.Text = player.Name
	label.Name = player.Name
	label.Text.TextColor = player.TeamColor
	if not player.Character then return end
	local char = player.Character
	local name = player.Name
	if registered[name] then unregister(name) end
	label.Parent = game.Players.LocalPlayer.PlayerGui
	label.Adornee = char:WaitForChild("Head")
	registered[name] = label
	RunService:BindToRenderStep(BaseName..name, Enum.RenderPriority.Camera.Value - 1, function()
		local distance = (workspace.CurrentCamera.CFrame.Position-char.Head.Position).Magnitude
		if distance > 90 and distance <= 100 then -- If more than 90 studs away, make transparency equal to studs in the unit spot divided by 10 (96.4->0.64)
			label.Text.TextTransparency = (distance-90)/10
		end
		label.Size = UDim2.new(label.Size.X.Scale, label.Size.X.Offset, 0, 23-(5*math.clamp(distance//25, 0, 3))) -- For every 25 studs from the character, subtract 5 from the size up to 3 times 
	end)
end
function playerAdded(player)
	if player == game.Players.LocalPlayer then return end
	if player.Character then register(player) end
	player.CharacterAdded:Connect(function()
		register(player)
	end)
	player.CharacterRemoving:Connect(function()
		unregister(player)
	end)
	player:GetPropertyChangedSignal("TeamColor"):Connect(function()
		local label = registered[player.Name]
		if label and label:FindFirstChild("Text") then
			label.Text.TextColor = player.TeamColor
		end
	end)
end
if not game:IsLoaded() then game.Loaded:Wait() end
for i,player in pairs(Players:GetPlayers()) do
	playerAdded(player)
end
Players.LocalPlayer.CharacterAdded:Connect(function()
	for i,player in pairs(game.Players:GetPlayers()) do
		unregister(player)
		task.wait()
		register(player)
	end
end)
Players.PlayerAdded:Connect(playerAdded)

If you have any better ways to update the size when moving I’d love to know

Type checking is a great feature to enable it on programs. Add --!strict at the top line of your program.

I don’t see a fading effect as having any use case. This only affects lower-end devices.

I don’t know the real benefits of this other than preference. The only benefit, I found is when I want to rename services (yes, I do that for some odd reason; sometimes.) It is a good habit to have as it may have other benefits.

Change:
local Players = game.Players
To:
local Players = game:GetService("Players")

Have fun programming! :smiley: