So, I was browsing through dev forum and saw that not many systems like this are made so I decided to make it. Also I was bored. Also Going to be a ton of hr spams lol. First time making a tutorial like this so yes.
The system looks something like that and if you want to know how to make it, read this post ig,
Oh yea if you want to test it out for yourself you can play it here - Pet Hatching Framework - Roblox
Before I start
You should know…
- Roblox Lua / Scripting
- Roblox’s Services (Not all, just TweenService and RunService)
- Use of modules (Using Modules to create main functions)
- CFrames
And other Stuff which I most definitely forgot about
Last Thing, This will be of two parts. One will contain the frame appearance and the egg hatching effect and the other will contain the inventory thing. Ok thanks
Lets Start shall we…
Leaderstats
So for the leaderstats, I will be sticking with one which is coins since i do not want to over complicate things.
Oh and for the dummies that do not know what this is, Its basically storing an Int value inside the player as its stats. It also appears on the right side of your screen.
To Start, Go ahead and make a script in ServerScriptService and call it whatever you want.
-- Variables --
local DS = game:GetService("DataStoreService")
local Players = game:GetService("Players")
local CoinsDataStore = DS:GetDataStore("CoinsDS")
local SM = require(script.PlayerStats)
Players.PlayerAdded:Connect(function(Player)
local Coins = SM.AddPlayerStats(Player)
local success, CoinData = pcall(function()
return CoinsDataStore:GetAsync(Player.UserId)
end)
Coins.Value = (type(CoinData) == "number" and CoinData) or 0
while wait(2) do
Coins.Value += 50
end
end)
Players.PlayerRemoving:Connect(function(Player)
local stats = Player.leaderstats
local Coins = stats.Coins.Value
CoinsDataStore:UpdateAsync(Player.UserId, function(oldData)
return Coins
end)
end)
Then make a module script inside the script and put this inside of it.
(Or i mean you just put it inside the server script but whatever, its easier to add more this way.)
local Stats = {}
function Stats.AddPlayerStats(Player)
local Folder = Instance.new("Folder", Player)
Folder.Name = "leaderstats"
local Coins = Instance.new('IntValue',Folder)
Coins.Name = "Coins"
return Coins
end
return Stats
A quick summary of this script does,
Basically its storing an int value in the player, and using datastores saves the players coins every time they leave.
Note - This Will Only Work On Studio If You Enable Datastore Access to Studio
Pet Making Time
Lets do something more fun.
We will set up all the props then script it to work which is something i like to do first.
So For the egg container I literally yeeted it out of an old project. There are a few important things you need to do however.
Suppose this is my egg, Keep all the decorative parts inside the Egg_Container and Keep the Egg mesh part (It will be important later on) outside that model.
Add a part inside the Egg and name it UI, and keep it outside the Egg_Container Folder too (basically the part will help use on the egg shops frame position.)
As for the position, just move it inside the egg and your good!
Then inside the Entire egg models Properties, add a number value attribute and call it Price - which will be the eggs price
.
Pet Time
So for pet models, you can make them in blender but I was lazy so I did not and just used the pet pack.
If you want a specific one, you can go ahead and use the one by @LuaBearyGood
Its a great pack containing of actually good pet models which you can use here and I highly recommend go checking it out - Pet Pack Devforum Post
Those pets were already Welded, Tho i changed a few things of it, Heres an example.
For Instance, Lets use this shark pet as an example
If we open its workspace, we see that it has quite some parts. The ones marked by red are all welded to the head, while the ones in blue are not (which is just main).
You can name the other parts anything you want just keep Head as Head and Main as Main (Main will be mentioned more in a bit ). Every Part in the pet should NOT be anchored And CanCollide should be false (unless you want your pet trying to enter your character )
Quick Tip - If you want to weld it all to the head, use moon animators weld in place feature, which is very useful in this situation
Also Head here being the center of the pet
Next we make this part called Main which is basically a normal roblox studio part
which should cover about the entire head of the pet.
Main should be anchored and also set to Cancollide false and Transparency to 1.
Lastly we add a weld Constraint to main, Where part0 is set to Main and part1 is set to the Head. Your pet Explorer should be looking like this
I wont be explaining why we are doing this or this post will be way to long, tho if you really want to know why i can do it on dms or down in the reply section.
After your done making your pets, make a folder in replicated storage called Pets and parent your pet to it
Indexs
Great My pets and Eggs are all made and ready to be used!
Now that we go those out of the way, lets start making our modules!
So I made a modules folder containing 2 more folders which are EggModules and PetModules. Right lets get started on making our index’s
This is just our pets Names, stats being declared in a script which will be used later on! So open a module script called Pet Index and parent it to pet modules
It should be looking something like this
local Rarities = {
WaterEgg = {
{
Name = "Shark",
Rarity = "Common",
Chance = 50,
},
{ Name = "AquaDragon",
Rarity = "Uncommon",
Chance = 35,
},
{
Name = "Babylightning",
Rarity = "Rare",
Chance = 10,
},
{
Name = "BacterialVirus",
Rarity = "Mythic",
Chance = 5,
},
},
["FireEgg"] = {
{
Name = "SmallDemon",
Rarity = "Common",
Chance = 55,
},
{
Name = "Demon",
Rarity = "Uncommon",
Chance = 30,
},
{
Name = "DemonDominus",
Rarity = "Rare",
Chance = 10,
},
{
Name = "Ultimus",
Rarity = "Mythic",
Chance = 5,
},
},
}
return Rarities
This will help us get the stats of each pet and also know which egg they belong too!
Lets also make an Egg Index module under Egg Modules, which will help use get the data for the eggs. It should be something like this
local Data = {}
local EggData = {}
local PetIndex = require(script.Parent.Parent.PetModules.PetIndex)
local Eggs = workspace:WaitForChild("Eggs")
function EggData.GetEgg(EggName)
if not Data[EggName] then
local Egg = Eggs:FindFirstChild(EggName)
local Data = PetIndex[EggName]
if Data then
local Chance = 0
for _, pet in pairs(PetIndex[EggName]) do
Chance += pet.Chance
end
EggData[EggName] = {
Name = EggName,
Price = Egg:GetAttribute("Price"),
PetData = Data,
TotalChance = Chance
}
return EggData[EggName]
end
end
end
return EggData
This is important as its gonna help us to obviously get the egg data. Small explaination of this script:
First we create the function Get Egg passing the parameter Egg Name, we check if Egg Name is not there, and then define data as pet index. Then using Data[EggName] we get the Name, Price of the Egg along with the chance and Pet Data of the pets!
Cool! (Jeez this sounds like a ramble instead of a tutorial, forgive me.)
Pet Frame Thing
Basically the frame is the pop-up that comes when you come close to the egg cointainer. (Low quality image ahhhh.)
This part will take a while since it has 4 scripts which need more modules etc. But basically this is the big part.
Ok so first things first insert
- Create a Screen Gui in Starter GUI and call it EggHatch
Insert a Number Value, Call it FarthestDistance and keep it in the Screen Gui
Add a String Value called CurrentEgg and keep in under the Screen Gui.
Ok lets start scripting.
Every script from now will be a local script as it will affect only the local player and not everybody else in the game.
So Create a local script called FrameHandler and paste the following code.
local Player = game.Players.LocalPlayer
local RS = game:GetService("RunService")
local Cam = workspace.Camera
local Frame = script.Parent.PurchaseMenu
local FrameSize = Frame.Size
local MaxDistance = script.Parent.FarthestDistance
local Eggs = workspace.Eggs:GetChildren()
local ChosenEgg = script.Parent.CurrentEgg
local Slot = Frame.SlotBackground.Slots
local Amount = Frame.AmountBackground.AmountBackground.CurrencyIcon.Cost
local EggIndex = require(game.ReplicatedStorage.Modules.EggModules.EggIndex)
local HatchingValue = game.ReplicatedStorage.Values.Hatching
local character = Player.Character or Player.CharacterAdded:Wait()
local Hrp = character:WaitForChild("HumanoidRootPart")
local function GetClosestEgg(CurrentEgg, HRP)
local closest
local currentClosest = MaxDistance.Value
for _, Eggs in ipairs(CurrentEgg) do
local distance = (Eggs.UI.Position - HRP.Position).Magnitude
if distance <= currentClosest then
currentClosest = distance
closest = Eggs
end
end
return closest
end
RS.RenderStepped:Connect(function()
if not Hrp:IsDescendantOf(workspace) then
character = Player.Character or Player.CharacterAdded:Wait()
Hrp = character:WaitForChild("HumanoidRootPart")
end
if HatchingValue.Value == false then
local camRatio =((Cam.CFrame.Position - Cam.Focus.Position).Magnitude)/11
local availableEggs = {}
for _, Egg in ipairs(Eggs) do
if Egg:IsA("Model") then
if (Egg.UI.Position - Hrp.Position).Magnitude <= MaxDistance.Value then
table.insert(availableEggs, Egg)
end
end
end
local closestEgg
if #availableEggs < 1 then
Frame.Visible = false
ChosenEgg.Value = "None"
return
elseif #availableEggs == 1 then
closestEgg = availableEggs[1]
elseif #availableEggs > 1 then
closestEgg = GetClosestEgg(availableEggs, Hrp)
end
if closestEgg then
ChosenEgg.Value = closestEgg.Name
local worldScreenPoint = Cam:WorldToScreenPoint(closestEgg.UI.Position)
Frame.Visible = true
Frame.Position = UDim2.fromOffset(worldScreenPoint.X, worldScreenPoint.Y)
Frame.Size = UDim2.new(FrameSize.X.Scale/camRatio, FrameSize.X.Offset, FrameSize.Y.Scale/camRatio, FrameSize.Y.Offset)
end
end
end)
HatchingValue:GetPropertyChangedSignal("Value"):Connect(function()
local Value = HatchingValue.Value
if Value then
Frame.Visible = false
else
Frame.Visible = true
end
end)
Quick Overview - It makes sure that the player is in a certain radius of the egg and then makes the frame visible and gives us the egg data.
Oh yea lets make the gui that shows up for the egg.
This is how i have made mine
And here’s the explorer tab for it.
And the final result of this script
Ah yes but it looks so empty, and the price value of the eggs dont come up
Yes yes , I’m getting to that
Ok now lets make a new script called slot handler which adds the pets to the viewport frames inside the slot.
For reference - Slot Explorer
Ok so inside the local script slot, paste this script which handles the entire slot as well as frame data.
local Slot = script.Parent.PurchaseMenu.SlotBackground.Slots
local ChosenEgg = script.Parent.CurrentEgg
local Frame = script.Parent.PurchaseMenu
local EggIndex = require(game.ReplicatedStorage.Modules.EggModules.EggIndex)
local EggEffect = require(game.ReplicatedStorage.Modules.EggModules.EggEffect)
local Amount = Frame.AmountBackground.AmountBackground.CurrencyIcon.Cost
local EggName = Frame.NameBackground.Background.TextLabel
local function resetFrame()
for _, slot in ipairs(Slot:GetChildren()) do
for _, item in ipairs(slot.PetView:GetChildren()) do
item:Destroy()
end
slot.Chance.Text = ""
slot.Rarity.Text = ""
end
end
ChosenEgg:GetPropertyChangedSignal("Value"):Connect(function()
if ChosenEgg.Value == "None" then
resetFrame()
Frame.Visible = false
else
local Egg = EggIndex.GetEgg(ChosenEgg.Value)
if Egg then
resetFrame()
Amount.Text = Egg.Price
EggName.Text = Egg.Name
Frame.Visible = true
EggEffect.AddPets(Slot, Egg)
end
end
end)
Anyway you should have gotten an error saying
EggEffect Does not exist :c
So, lets create a new module called EggEffect and add it to the eggmodules folder.
Then go ahead and add this to it.
local TS = game:GetService("TweenService")
local RS = game:GetService("RunService")
local EggDataModule = require(game.ReplicatedStorage.Modules.EggModules:WaitForChild("EggIndex"))
local RarityColors = require(game.ReplicatedStorage.Modules.PetModules.PetRarityColors)
-- Effects --
local Blur = game.Lighting.Blur
local EF = workspace:WaitForChild("Eggs")
local Pets = game.ReplicatedStorage.Pets
local EggViewportHandler = {}
function EggViewportHandler.AddPets(slots, Egg)
for i, pet in pairs(Egg.PetData) do
local petClone = Pets:FindFirstChild(pet.Name):Clone()
local currentSlot = slots:FindFirstChild("Slot" .. i)
local Color = RarityColors[pet.Rarity]
local viewport = currentSlot.PetView
local camera = Instance.new("Camera", viewport)
viewport.CurrentCamera = camera
petClone:SetPrimaryPartCFrame(CFrame.new(0, 0, 0))
petClone.Parent = viewport
currentSlot.Chance.Text = pet.Chance .. "%"
currentSlot.Rarity.Text = pet.Rarity
currentSlot.Rarity.TextColor3 = Color
coroutine.wrap(function()
local Rotation = 0
local distance = petClone.PrimaryPart.Size.Z
while petClone.Parent do
camera.CFrame = CFrame.Angles(0, math.rad(Rotation), 0) * CFrame.new(Vector3.new(0, 0, distance), Vector3.new(0, 0, 0))
Rotation += 0.5
RS.RenderStepped:Wait()
end
end)()
end
end
Oh and if you get an error that rarity colors isnt existing or blur dosent exist. You can remove those lines as those will be used later on in the script. Otherwise if you really want to put it then add a Module script called PetRarityColors in the Petmodules folder and paste this script.
local PetRarityColors = {
Common = Color3.fromRGB(85, 255, 127),
Uncommon = Color3.fromRGB(223, 201, 34),
Rare = Color3.fromRGB(255, 47, 47),
Mythic = Color3.fromRGB(211, 79, 255),
}
return PetRarityColors
Its basically helping us get the colors for the rarities.
Dont Forget to add Blur to lighting for this to not give errors k thx
Anyway if you did it right, if should work like this.
If you reached all the way here, give yourself a pat on your back. Good Job!
Ok now alot of this next code is going to be the back end except like the effect. The scripts left are
- The Egg Purchase
- The Egg Purchase Check script
- Random Pet Choosing
- The Hatch Event
- The Egg Effect
Egg Purchase Check
``
Import a module script called EggPurchaseCheck in EggModules and paste the following Code
local EggIndex = require(script.Parent:WaitForChild("EggIndex"))
local EggPurchaseModule = {}
function EggPurchaseModule.Check(player, eggName, amount, eggdata)
local price
if eggdata then
price = eggdata.Price
else
price = EggIndex.FindEgg(eggName)
end
if price then
local PlayerCoinStats = player.leaderstats:FindFirstChild("Coins")
local Check = false
repeat
if PlayerCoinStats.Value >= price * amount then
Check = true
PlayerCoinStats.Value -= price
else
amount -= 1
end
until (amount < 1) or Check
if Check then
return (eggdata and amount) or true
else
return print(player.Name .. " Has insufficient funds!")
end
else
return print("Could not find price of " .. eggName)
end
end
return EggPurchaseModule
This will record the players egg purchase and will be used in the main server script.
Egg Purchase Script
This script will do 2 things.
Checking the player has bought the egg with the above module.
And if bought, will fire the Hatch remote event. So in Replicated storage Add 2 folders, Namely - Remotes And Values.
Add a remote event Called EggOpen in Remote.
Add a Number Value In Values Called EggOpenCooldown and Bool Value Called Hatching.
Ok, now lets make the egg purchase script so add a script to server script service and paste the following code.
-- Services --
local Player = game:GetService("Players")
local RS = game:GetService("ReplicatedStorage")
-- Modules --
local EggHandler = require(RS.Modules.EggModules.EggHandler)
-- Remotes --
local HatchEvent = RS.Remotes.EggOpen
-- Controllers --
local cooldowns = {}
local CooldownValue = game.ReplicatedStorage.Values.EggOpenCooldown
local function hatch(player, eggName)
if not cooldowns[player] then
cooldowns[player] = time()
end
if time() >= cooldowns[player] then
cooldowns[player] = time() + CooldownValue.Value
local newPets = EggHandler.Purchase(player, eggName, 1)
HatchEvent:FireClient(player, newPets, eggName)
end
end
HatchEvent.OnServerEvent:Connect(hatch)
Random Pet Choosing
Ok so you must have seen that in the script above the EggHandler module is called.
That script is the random pet choosing script.
So make a module script called EggHandler in EggModules and put this code that will make the random value for pets work
local EggData = require(script.Parent.EggIndex)
local EggPurchaseCheck = require(script.Parent.EggPurchaseCheck)
local function RandomPet(pets, Chance)
local luck = math.random(Chance)
for _, pet in ipairs(pets) do
if luck > pet.Chance then
luck -= pet.Chance
else
return pet.Name, pet.Rarity
end
end
end
local EggPurchaseHandler = {}
function EggPurchaseHandler.Purchase(Player, EggName, Amount)
local Egg = EggData.GetEgg(EggName)
local canBuy = EggPurchaseCheck.Check(Player, EggName, Amount, Egg)
if canBuy then
local newPets = {}
repeat
local newPet, petRarity = RandomPet(Egg.PetData, Egg.TotalChance)
table.insert(newPets, {Name = newPet, Rarity = petRarity, EggName = Egg})
until #newPets >= Amount
return newPets
end
end
return EggPurchaseHandler
Ok Almost every module script is completely done except the Egg Effect which we will do last.
The Hatch Event
So as you can see we fired the even from the server script. Next we need to mention what the event does when it is fired.
So add a local script to the EggHatch Screen Gui and call it Hatch Handler.
local Player = game:GetService("Players").LocalPlayer
local CAS = game:GetService("ContextActionService")
local EggPurchase = require(game.ReplicatedStorage.Modules.EggModules.EggPurchaseCheck)
local EggIndex = require(game.ReplicatedStorage.Modules.EggModules.EggIndex)
local EggEffect = require(game.ReplicatedStorage.Modules.EggModules.EggEffect)
local Frame = script.Parent.PurchaseMenu
local buttons = Frame.Buttons
local Ebutton = buttons.E.Button
local hatchEvent = game.ReplicatedStorage.Remotes.EggOpen
local eggs = workspace.Eggs
local chosenEgg = script.Parent.CurrentEgg
local Cooldown = game.ReplicatedStorage.Values.EggOpenCooldown
local cooldown = time()
local Hatching = game.ReplicatedStorage.Values.Hatching
local function hatch(_ , inputState, inputObject)
if inputState == Enum.UserInputState.Begin and time() >= cooldown and Hatching.Value == false then
cooldown = time() + Cooldown.Value
local EggData = EggIndex.GetEgg(chosenEgg.Value)
local result = EggPurchase.Check(Player, chosenEgg.Value, 1,EggData)
if result then
Hatching.Value = true
hatchEvent:FireServer(chosenEgg.Value, false)
end
end
end
chosenEgg:GetPropertyChangedSignal("Value"):Connect(function()
local Egg = chosenEgg.Value
if Egg ~= "None" then
CAS:BindAction("Hatch", hatch, false, Enum.KeyCode.E)
else
CAS:UnbindAction("Hatch")
end
end)
Ebutton.MouseButton1Up:Connect(function()
hatch(nil, Enum.UserInputState.Begin, Enum.KeyCode.E)
end)
This will control when it supposed to reach the server. (like pressing E and being close to the egg.)
And add the Final local script to the EggHatch Screen gui and call it EggHatch lol.
Then Add this
local hatchevent = game.ReplicatedStorage.Remotes.EggOpen
local Hatching = game.ReplicatedStorage.Values.Hatching
local EggEffect = require(game.ReplicatedStorage.Modules.EggModules.EggEffect)
hatchevent.OnClientEvent:Connect(function(newPets, eggName)
local NewFrame = script.Parent.Parent.Hatch.HatchFrame:Clone()
NewFrame.Parent = script.Parent.Parent.HatchGui
script.Parent.PurchaseMenu.Visible = false
EggEffect.HatchPets(newPets, NewFrame, eggName)
Hatching.Value = false
end)
This is basically the hatching part after you press e and have sufficient coins.
And GG your done with everything except the Egg Effect which is the last thing
Egg Effect
Oh boy, this is gonna take a while since this part is a little big.
In the EggEffect Module Add copy this entire function bellow the AddPets Function
local function TweenEgg(Obj,t,s,d, Props)
return TS:Create(Obj,TweenInfo.new(t,s,d,0,false,0),Props)
end
-- Basically Tweening the Rotation of the Egg.
local function RotateEgg(egg, rotation, t)
local Cframe = CFrame.new(egg.Position) * CFrame.Angles(0, 0, math.rad(rotation))
local rotateTween = TweenEgg(egg, t , Enum.EasingStyle.Linear,Enum.EasingDirection.In, {CFrame = Cframe})
rotateTween:Play()
rotateTween.Completed:Wait()
rotateTween:Destroy(); rotateTween = nil
end
local positions = {
{Rotation = math.rad(0), Cframe = CFrame.new(0, -10, 0)},
{Rotation = math.rad(45), Cframe = CFrame.new(-4.5, -10, 0)},
{Rotation = math.rad(-45), Cframe = CFrame.new(4.5, -10, 0)},
}
function EggViewportHandler.HatchPets(newPets, HatchFrame, EggName)
Blur.Size = 26
Blur.Enabled = true
local camera = Instance.new("Camera", HatchFrame)
camera.CFrame = CFrame.new(0, 0, 6)
HatchFrame.CurrentCamera = camera
local numberOfPets = #newPets
local petsHatched = 0
for i, v in ipairs(newPets) do
coroutine.wrap(function()
local petName = v.Name
local rarity = v.Rarity
local Pet = Pets:FindFirstChild(petName, true):Clone()
local Egg = EF:FindFirstChild(EggName).Egg:Clone()
local Rotation = (numberOfPets > 1 and positions[i].Rotation) or positions[3].Rotation/2
local cframe = positions[i].Cframe
Egg.Parent = HatchFrame
Egg.CFrame = cframe
local liftEggTween = TweenEgg(Egg, 0.65, Enum.EasingStyle.Quad,Enum.EasingDirection.In, {Position = Egg.Position + Vector3.new(0, 10, 0)})
liftEggTween:Play()
liftEggTween.Completed:Wait()
liftEggTween:Destroy(); liftEggTween = nil
local delayTime = 0.11
for inc = 1, 10 do
RotateEgg(Egg, -15, delayTime)
RotateEgg(Egg, 15, delayTime)
delayTime -= 0.007
end
RotateEgg(Egg, 0, delayTime)
local flashEffect = HatchFrame["1"].FlashEffect
-- flashEffect.ImageColor3 = (rarity ~= "Common") or flashEffect.ImageColor3
local flash = TweenEgg(flashEffect, 0.2 , Enum.EasingStyle.Linear,Enum.EasingDirection.Out, {ImageTransparency = 1, Size = UDim2.new(1.5, 0, 1.5, 2)})
flash:Play()
flash.Completed:Wait()
flash:Destroy(); flash = nil
Pet.Parent = HatchFrame
Pet:SetPrimaryPartCFrame(CFrame.new(0,0,0) * CFrame.Angles(0, 1.35, 0))
Egg:Destroy()
local Color = RarityColors[v.Rarity]
HatchFrame[i].PetName.Text = petName
HatchFrame[i].PetRarity.Text = rarity
HatchFrame[i].PetRarity.TextColor3 = Color
wait(1)
local size = Blur.Size/10
for i = 1, 10 do
Blur.Size -= size
wait(0.05)
end
HatchFrame[i].PetName.Text = ""
HatchFrame[i].PetRarity.Text = ""
petsHatched += 1
print("You hatched a " .. petName)
end)()
end
repeat
RS.RenderStepped:Wait()
until petsHatched >= numberOfPets
local lowerFrameTween = TweenEgg(HatchFrame, 0.2, Enum.EasingStyle.Quad,Enum.EasingDirection.Out, {Position = UDim2.fromScale(0, 1)})
lowerFrameTween:Play()
lowerFrameTween.Completed:Wait()
lowerFrameTween:Destroy()
lowerFrameTween = nil
HatchFrame:Destroy()
end
This is the entire egg Effect Which will make the egg tween up , rotate , open and then tween back down.
I suggest actually reading the entire script so its easier to understand and know whats up but yea heres the entire framework for part 1.
If you have any questions / comments or any sort of criticism for this post, tell me bellow. This is my first or second community resource and ill be glad to know what i can work on. Thank you so much and have a wonder full day!