Roblox · Pet Simulator · урок 3

Яйца с питомцами

Яйцо за монеты: подходишь, жмёшь E — на экране яйцо качается, лопается, и появляется выпавший питомец с редкостью. Кто выпадет — решают шансы в таблице.

Структура

Синим — что добавляем на этом уроке.

Workspace
└─ Eggs (Folder)
   └─ Start Egg (Part / MeshPart) — яйцо, Anchored
      └─ ProximityPrompt (ProximityPrompt)
ReplicatedStorage
└─ Events (Folder)
   └─ HatchEggEvent (RemoteEvent)
ServerScriptService
├─ MainScript → PlayerInfo (Folder)
│  └─ Pets (Folder) — купленные питомцы (StringValue)
└─ EggsServer (Script)
   └─ EggInfo (ModuleScript)
StarterGui
└─ EggSystemGui (ScreenGui) — Enabled выкл., ResetOnSpawn выкл.
   └─ EggViewport (ViewportFrame) — AnchorPoint 0.5, 0.5, Position 0.5, 0, 0.5, 0
      └─ EggName, EggRarity (TextLabel) — Visible выкл.
StarterPlayer → StarterPlayerScripts
└─ EggsLocal (LocalScript)

Шаги

  1. Яйцо: Toolbox → фильтр Meshes → яйцо, имя «Start Egg», Anchored; внутрь — ProximityPrompt (HoldDuration 0.5). Workspace → Folder «Eggs», яйцо туда.
  2. В PlayerInfo — Folder «Pets»: сюда сервер кладёт StringValue с именем каждого выпавшего питомца.
  3. StarterGui → ScreenGui «EggSystemGui» (Enabled выкл., ResetOnSpawn выкл.) → ViewportFrame «EggViewport» (AnchorPoint 0.5, 0.5, по центру экрана) → TextLabel «EggName» и «EggRarity» внизу окна (Visible выкл.).
  4. ViewportFrame показывает 3D-объекты прямо в интерфейсе: кладём в него копию яйца или питомца и камеру.
  5. ReplicatedStorage → Events → RemoteEvent «HatchEggEvent».
  6. ServerScriptService → Script «EggsServer», в него ModuleScript «EggInfo». Имена яиц в EggInfo — как в папке Eggs, имена питомцев — как в Pets. Chance — вес: сумма может быть любой.
  7. StarterPlayerScripts → LocalScript «EggsLocal».
  8. Питомец пока только записывается в папку Pets — надевать его научимся в инвентаре.

Скрипты

EggInfo

Где: ServerScriptService → EggsServer · ModuleScript
Зачем: Цены яиц и шансы питомцев.

-- все яйца
local eggData = {
    -- имя — как у яйца в папке Eggs
    ["Start Egg"] = {
        -- цена
        EggPrice = 10,
        -- валюта: Coins или Gems
        EggMoney = "Coins",
        -- кто может выпасть
        EggPets = {
            -- имя — как у модели в Pets; Chance — вес
            {PetName = "Bunny", Chance = 50, Rarity = "Common"},
            -- ещё один
            {PetName = "Cat", Chance = 50, Rarity = "Common"},
        -- конец списка
        },
    -- конец яйца
    },
    -- яйцо за кристаллы
    ["Gem Egg"] = {
        -- цена
        EggPrice = 5,
        -- валюта
        EggMoney = "Gems",
        -- кто может выпасть
        EggPets = {
            -- чаще
            {PetName = "Dog", Chance = 70, Rarity = "Uncommon"},
            -- реже
            {PetName = "Dragon", Chance = 30, Rarity = "Rare"},
        -- конец списка
        },
    -- конец яйца
    },
-- конец таблицы
}

-- отдаём таблицу
return eggData

EggsServer

Где: ServerScriptService · Script
Зачем: Покупка яйца и случайный питомец.

-- данные яиц
local eggData = require(script.EggInfo)
-- общее хранилище
local RS = game:GetService("ReplicatedStorage")
-- яйца на карте
local eggsFolder = workspace:WaitForChild("Eggs")
-- показать анимацию игроку
local hatchEggEvent = RS:WaitForChild("Events"):WaitForChild("HatchEggEvent")
-- кто сейчас открывает яйцо
local hatching = {}

-- случайный питомец с учётом шансов
local function SelectRandomPet(petTable)
    -- сумма шансов
    local total = 0
    -- складываем — не обязательно 100
    for _, info in ipairs(petTable) do total += info.Chance end
    -- случайное число
    local roll = math.random(1, total)
    -- накопленный шанс
    local sum = 0
    -- по питомцам
    for _, info in ipairs(petTable) do
        -- прибавляем
        sum += info.Chance
        -- число попало на этого питомца
        if roll <= sum then return info end
    -- конец перебора
    end
-- конец функции
end

-- каждое яйцо
for _, egg in ipairs(eggsFolder:GetChildren()) do
    -- его данные
    local info = eggData[egg.Name]
    -- надпись «Купить»
    local prompt = egg:FindFirstChildWhichIsA("ProximityPrompt", true)
    -- чего-то нет
    if not info or not prompt then
        -- подсказка в Output
        warn("Яйцо " .. egg.Name .. ": нет данных в EggInfo или ProximityPrompt")
    -- всё есть
    else
        -- название
        prompt.ObjectText = egg.Name
        -- цена на надписи
        prompt.ActionText = info.EggPrice .. " " .. info.EggMoney
        -- игрок нажал E
        prompt.Triggered:Connect(function(player)
            -- уже открывает — ждём
            if hatching[player] then return end
            -- нужная валюта
            local money = player.leaderstats:FindFirstChild(info.EggMoney)
            -- не хватает
            if not money or money.Value < info.EggPrice then return end
            -- открывает
            hatching[player] = true
            -- списываем
            money.Value -= info.EggPrice
            -- кто выпал
            local selected = SelectRandomPet(info.EggPets)
            -- запись о питомце
            local petValue = Instance.new("StringValue")
            -- имя
            petValue.Name = selected.PetName
            -- в папку Pets игрока
            petValue.Parent = player.Pets
            -- анимация у игрока
            hatchEggEvent:FireClient(player, egg.Name, selected.PetName, selected.Rarity)
            -- пока идёт анимация
            task.wait(5)
            -- можно открыть следующее
            hatching[player] = nil
        -- конец обработчика
        end)
    -- конец проверки
    end
-- конец перебора
end

EggsLocal

Где: StarterPlayer → StarterPlayerScripts · LocalScript
Зачем: Анимация открытия яйца.

-- общее хранилище
local RS = game:GetService("ReplicatedStorage")
-- анимации
local TS = game:GetService("TweenService")
-- яйца
local eggsFolder = workspace:WaitForChild("Eggs")
-- образцы питомцев
local pets = RS:WaitForChild("Pets")
-- сигнал от сервера
local hatchEggEvent = RS:WaitForChild("Events"):WaitForChild("HatchEggEvent")
-- этот игрок
local player = game.Players.LocalPlayer
-- его интерфейс
local playerGui = player:WaitForChild("PlayerGui")
-- окно яйца
local eggSystemGui = playerGui:WaitForChild("EggSystemGui")
-- 3D-окошко
local eggViewport = eggSystemGui:WaitForChild("EggViewport")
-- имя питомца
local eggName = eggViewport:WaitForChild("EggName")
-- редкость
local eggRarity = eggViewport:WaitForChild("EggRarity")
-- размер окна
local FULL = UDim2.fromScale(0.3, 0.5)
-- окно схлопнуто
local HIDDEN = UDim2.fromScale(0, 0)

-- убрать из окошка яйцо, питомца и камеру
local function Clear()
    -- всё внутри
    for _, obj in ipairs(eggViewport:GetChildren()) do
        -- камеры, детали, модели (надписи остаются)
        if obj:IsA("Camera") or obj:IsA("PVInstance") then obj:Destroy() end
    -- конец перебора
    end
-- конец функции
end

-- поставить объект в окошко
local function Show(object)
    -- старое убираем
    Clear()
    -- новый объект
    object.Parent = eggViewport
    -- его размер
    local size = object:IsA("Model") and object:GetExtentsSize() or object.Size
    -- камера окошка
    local camera = Instance.new("Camera")
    -- отходим тем дальше, чем больше объект
    camera.CFrame = CFrame.new(0, 0, size.Magnitude * 1.1)
    -- в окошко
    camera.Parent = eggViewport
    -- окошко смотрит через неё
    eggViewport.CurrentCamera = camera
-- конец функции
end

-- плавно меняет размер окна
local function Resize(size)
    -- за полсекунды
    local tween = TS:Create(eggViewport, TweenInfo.new(0.5), {Size = size})
    -- запускаем
    tween:Play()
    -- ждём
    tween.Completed:Wait()
-- конец функции
end

-- наклон окна — яйцо качается
local function Rotate(rotation)
    -- за 0,3 с
    local tween = TS:Create(eggViewport, TweenInfo.new(0.3, Enum.EasingStyle.Sine), {Rotation = rotation})
    -- запускаем
    tween:Play()
    -- ждём
    tween.Completed:Wait()
-- конец функции
end

-- вся анимация открытия
local function HatchEgg(eggNameText, petName, petRarity)
    -- интерфейсы, которые прячем
    local hidden = {}
    -- весь интерфейс игрока
    for _, ui in ipairs(playerGui:GetChildren()) do
        -- включённый и не окно яйца
        if ui:IsA("ScreenGui") and ui ~= eggSystemGui and ui.Enabled then
            -- прячем
            ui.Enabled = false
            -- запоминаем, чтобы вернуть только их
            table.insert(hidden, ui)
        -- конец проверки
        end
    -- конец перебора
    end
    -- надписи пока скрыты
    eggName.Visible = false
    -- тоже
    eggRarity.Visible = false
    -- окно схлопнуто
    eggViewport.Size = HIDDEN
    -- включаем окно яйца
    eggSystemGui.Enabled = true
    -- копия яйца
    local egg = eggsFolder:FindFirstChild(eggNameText):Clone()
    -- в центр окошка
    egg:PivotTo(CFrame.new())
    -- показываем
    Show(egg)
    -- окно раскрывается
    Resize(FULL)
    -- два раза
    for _ = 1, 2 do
        -- качнулось вправо
        Rotate(20)
        -- влево
        Rotate(-20)
    -- конец цикла
    end
    -- ровно
    Rotate(0)
    -- окно схлопнулось — яйцо «лопнуло»
    Resize(HIDDEN)
    -- образец питомца
    local template = pets:FindFirstChild(petName)
    -- есть
    if template then
        -- копия
        local petModel = template:Clone()
        -- мордой к камере
        petModel:PivotTo(CFrame.Angles(0, math.rad(180), 0))
        -- показываем
        Show(petModel)
    -- конец проверки
    end
    -- имя
    eggName.Text = petName
    -- редкость
    eggRarity.Text = petRarity
    -- показываем
    eggName.Visible = true
    -- показываем
    eggRarity.Visible = true
    -- окно раскрывается
    Resize(FULL)
    -- любуемся
    task.wait(3)
    -- схлопываем
    Resize(HIDDEN)
    -- чистим
    Clear()
    -- выключаем окно
    eggSystemGui.Enabled = false
    -- возвращаем спрятанные интерфейсы
    for _, ui in ipairs(hidden) do ui.Enabled = true end
-- конец функции
end

-- сервер прислал питомца — анимация
hatchEggEvent.OnClientEvent:Connect(HatchEgg)

Проверка

Запусти Play (или Run) и убедись: