Skip to content
ENSign in

How do I make an egg hatching system in Roblox?

An egg is three things: a price, a weighted table of pets, and an animation. The server takes the coins, rolls the table, writes the pet to the player's inventory and sends back which pet it was. The client plays the crack and reveals that pet. Nothing is decided on the client.

In short

  • Charge, roll and save in that order, all on the server.
  • One weighted table per egg, so a new egg is data and not code.
  • The reveal animation reads the result; it never produces it.

A hatch is a purchase with a surprise attached. The surprise is the fun part and the purchase is the part that has to be right: coins leave the account once, the pet arrives once, and a player who closes the game mid-animation still owns the pet when they come back.

Steps

1. Describe the egg as data

One table per egg, with its price and its pets. Adding the Volcano Egg should be eight lines of data, not a new script.

local EGGS = {
	basic = {
		price = 250,
		pets = {
			{ id = "dog",    weight = 500 },
			{ id = "cat",    weight = 300 },
			{ id = "bunny",  weight = 150 },
			{ id = "dragon", weight = 49 },
			{ id = "void",   weight = 1 },
		},
	},
	volcano = {
		price = 5000,
		pets = {
			{ id = "magma",   weight = 600 },
			{ id = "obsidian", weight = 300 },
			{ id = "phoenix",  weight = 95 },
			{ id = "void",     weight = 5 },
		},
	},
}

2. Roll the table

The same weighted roll a spin wheel uses. Keep it in one place: an egg and a wheel disagreeing about odds is a bug nobody finds.

local rng = Random.new()

local function rollPet(pets): string
	local total = 0
	for _, p in pets do
		total += p.weight
	end
	local ticket = rng:NextInteger(1, total)
	local running = 0
	for _, p in pets do
		running += p.weight
		if ticket <= running then
			return p.id
		end
	end
	return pets[#pets].id
end

3. Charge, then save, then answer

The order is the whole design. Take the coins before rolling, so a player cannot hatch for free by firing the remote twice; save before answering, so the pet exists before the player sees it.

local Players = game:GetService("Players")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local DataStoreService = game:GetService("DataStoreService")

local pets = DataStoreService:GetDataStore("Pets_v1")
local hatch = Instance.new("RemoteEvent")
hatch.Name = "HatchEgg"
hatch.Parent = ReplicatedStorage

local busy: { [number]: boolean } = {}

hatch.OnServerEvent:Connect(function(player: Player, eggId: unknown)
	-- Never trust the egg id: it came from the client.
	local egg = type(eggId) == "string" and EGGS[eggId] or nil
	if not egg then return end
	-- One hatch at a time per player: two remotes in the same tick would both
	-- read the same coin balance and both pass.
	if busy[player.UserId] then return end
	busy[player.UserId] = true

	local ok = pcall(function()
		local stats = player:FindFirstChild("leaderstats")
		local coins = stats and stats:FindFirstChild("Coins")
		if not coins or coins.Value < egg.price then
			error("not enough")
		end
		coins.Value = coins.Value - egg.price

		local petId = rollPet(egg.pets)
		pets:UpdateAsync("u_" .. player.UserId, function(old)
			local owned = type(old) == "table" and old or {}
			owned[petId] = (owned[petId] or 0) + 1
			return owned
		end)
		hatch:FireClient(player, eggId, petId)
	end)
	busy[player.UserId] = nil
	if not ok then
		hatch:FireClient(player, eggId, nil)
	end
end)

Players.PlayerRemoving:Connect(function(player)
	busy[player.UserId] = nil
end)

4. Open the egg on the client

The animation is pure show. It takes the pet id it was given and reveals that model — if the player leaves halfway, the pet is already theirs.

local ReplicatedStorage = game:GetService("ReplicatedStorage")
local TweenService = game:GetService("TweenService")

local hatch = ReplicatedStorage:WaitForChild("HatchEgg")
local models = ReplicatedStorage:WaitForChild("PetModels")

hatch.OnClientEvent:Connect(function(_eggId: string, petId: string?)
	if not petId then return end -- refused: not enough coins
	local model = models:FindFirstChild(petId)
	if not model then return end

	local shown = model:Clone()
	shown.Parent = workspace
	shown:PivotTo(workspace.CurrentCamera.CFrame * CFrame.new(0, 0, -6))
	for _, part in shown:GetDescendants() do
		if part:IsA("BasePart") then
			part.Anchored = true
			part.CanCollide = false
			part.Transparency = 1
			TweenService:Create(part, TweenInfo.new(0.6), { Transparency = 0 }):Play()
		end
	end
	task.delay(3, function()
		shown:Destroy()
	end)
end)

Where to keep the pet a player owns

WhereSurvives a rejoinSafe from the clientUse it when
DataStore, counted per pet idyesyesThe default: a table of { petId = howMany }
A folder of values under the playernoyesOnly as the live mirror of the saved table
A client-side tablenonoNever
An ordered DataStoreyesyesOnly for a leaderboard of how many were hatched

FAQ

Should the pet be rolled on hatch or when the egg is bought?

On hatch, which in this design is the same moment. What must not happen is rolling it when the egg appears in the shop: a result that exists before the player presses the button is a result something on the client can read.

Why is my player sometimes charged with no pet?

Because the charge and the save are two separate writes and one of them failed. The pcall above puts them in one block and refuses the whole hatch, so a failed save gives the coins back by never having committed the roll. If you split them, you own that bug.

How do I show the odds?

Print them from the same table the roll uses — weight / total as a percentage, rounded to one decimal. Typing the percentages into the UI by hand is how a 1% pet quietly becomes a 0.2% pet after somebody adds a sixth pet.

Can I use one egg script for ten eggs?

That is the point of step 1. The script takes an egg id, looks it up and rolls. Ten eggs means ten entries in EGGS, and a new egg cannot introduce a new bug because there is no new code.

Updated: Tested in Studio: Roblox Studio, October 2026Author: GustavoLicense: Code in this guide may be used in any Roblox experience, including a paid one

Related pack

Brainrot Pet Pack

Brainrot Pet Pack

$150See the pack

Get notified

Coming soon

Drop your email and we'll ping you the moment this pack goes live.