Build the upgrade shop's interface — a panel frame, a buy button, and a label that mirrors the server's data.
The purchase logic is secure. Now give it a face: a shop panel — a Frame that holds the price label, a buy button, and the player's balance, all of it reflecting what the server actually recorded.
A Frame groups elements together and gives them a visual home:
local panel = Instance.new("Frame")
panel.Size = UDim2.new(0, 260, 0, 160)
panel.Position = UDim2.new(1, -20, 0.5, 0)
panel.AnchorPoint = Vector2.new(1, 0.5)
panel.BackgroundColor3 = Color3.fromRGB(30, 30, 40)
panel.Parent = screenGui
local corner = Instance.new("UICorner")
corner.CornerRadius = UDim.new(0, 12)
corner.Parent = panel
AnchorPoint = (1, 0.5) pins the panel's right edge to the right side of the screen — Position = UDim2.new(1, -20, 0.5, 0) sits it 20 pixels in from the edge.
What does AnchorPoint = Vector2.new(1, 0.5) do to the panel?
A child's Position is measured from the top-left of its parent frame, not the screen:
local title = Instance.new("TextLabel")
title.Size = UDim2.new(1, 0, 0, 30)
title.Position = UDim2.new(0, 0, 0, 8)
title.Text = "Upgrades"
title.Font = Enum.Font.GothamBold
title.Parent = panel
local buyButton = Instance.new("TextButton")
buyButton.Size = UDim2.new(0, 160, 0, 40)
buyButton.Position = UDim2.new(0.5, 0, 1, -50)
buyButton.AnchorPoint = Vector2.new(0.5, 0)
buyButton.Text = "Buy multiplier"
buyButton.Parent = panel
The buy button sits at Y = 1, -50 — the frame's height minus 50 — so it hugs the bottom of the panel. Anchoring relative to the parent keeps the whole panel moveable as one unit.
A child's Position is measured from the top-left corner of its , not the screen.
buyButton.Activated:Connect(function()
purchaseEvent:FireServer("multiplier")
end)
purchaseEvent.OnClientEvent:Connect(function(coins, multiplier)
balanceLabel.Text = "Coins: " .. coins
buyButton.Text = "Multiplier x" .. multiplier
end)
One handler for both outcomes — success or refusal — it just reflects whatever the server actually recorded. That's the beauty of the design: the client's UI is always a mirror of the server's data, so it can't drift out of sync.
After a purchase, the shop should show the new multiplier level on the buy button. Write the line that sets its text.
-- inside the OnClientEvent handler
Parent set to the panel or it never renders.Next: your scripts are getting big — time to organize with ModuleScripts.