Описанные ранее способы монетизации конечно работают. Но вот пришла беда, откуда не ждали! Они «теряются» если персонаж умирает! Или когда игрок жмёт «Reset character». Вот уж действительно — нежданчик.
Пришлось пересмотреть подход к сохранению информации о покупки конкретным игроком.
Во первых данная информация должна хранится на сервере.
Во вторых данная информация должна загружаться после смерти персонажа.
В третьих там должна храниться информация и о временных покупках, если они накладывают какой-то эффект на самого персонажа.
Варианты были разные. Но у всех были какие-либо недостатки и тут меня осенило! Единственное что не изменяется пока игрок не вышел это сам игрок! Провёл небольшое тестирование и вот оно — решение найдено = нужно хранить данные о покупках в переменных, которые будут лежать в самом игроке!
Но, так как при входе игрока этих переменных нет, то их надо создать. А также нужно их читать, когда игрок воскрешается.
Для начала, переделал код для GamePass-а:
--[[ https://developer.roblox.com/en-us/api-reference/event/Player/CharacterAdded https://developer.roblox.com/en-us/api-reference/event/Player/CharacterRemoving/index.html https://developer.roblox.com/en-us/api-reference/property/Player/Character/index.html https://developer.roblox.com/en-us/api-reference/event/IntValue/Changed ]]-- local Players = game:GetService("Players") local RunService = game:GetService("RunService") local tmp local id1 = 8626045 -- id gamepass - 5s -- ищем игрока -- добавляем ему флаг, что оно куплено -- убираем с экрана возможность покупки local function buy(tmp) local plr = game.Players:FindFirstChild(tmp) local char = plr.Character:WaitForChild("HumanoidRootPart") RunService.Stepped:wait() char = plr.Character if char ~= nil then -- игрок не покинул игру if game:GetService("MarketplaceService"):UserOwnsGamePassAsync(game.Players[char.Name].UserId, id1) then -- что тут нужно сделать?! char.Humanoid.WalkSpeed = 30 if plr:FindFirstChild("Buy") == nil then -- проверяем наличие переменной, если нет - создаём local Buy = Instance.new("IntValue") Buy.Parent = plr Buy.Name = "Buy" Buy.Value = 5 end -- а теперь убираем Frame покупки local pl = game.Players:FindFirstChild(plr.Name) if pl ~= nil then pl.PlayerGui.Main.GamePass.Visible=false pl.PlayerGui.Main.Answer.Visible=true end end end end -- проверка уже приобретённого пропуска при подключении игрока game.Players.PlayerAdded:Connect(function(plr) plr.CharacterAdded:connect(function(char) buy(char.Name) end) plr.CharacterRemoving:Connect(function(char) buy(char.Name) local pl = plr -- Players:GetPlayerFromCharacter(char) if pl:FindFirstChild("Buy") ~= nil then -- проверяем наличие переменной if pl.Buy.Value == 3 then pl.PlayerGui.Main.GameDev.Visible=false pl.PlayerGui.Main.Answer.Visible=true end end end) end) -- проверка на совершении покупки game:GetService("MarketplaceService").PromptGamePassPurchaseFinished:Connect(function(plr,ido,purchased) if purchased and ido == id1 then -- пропуск с id1 -- local char = plr.Character buy(plr.Name) end end)
Как можно заметить, я немножко оптимизировал. Вынес код необходимых действий из блоков на вход игрока и покупку игроком GamePass в самой игре в отдельный блок, которому в качестве параметра просто передаётся имя игрока.
В подключении игрока теперь не только создание чара — CharacterAdded, но и его удаление — CharacterRemoving. Это удаление и происходит при смерти персонажа или когда нажата вышеупомянутая кнопка.
Так же добавил «уборку» с экрана возможности покупки GamePass-а, если он у нас уже куплен. Нечего ему глаза мозолить.
Так как у GamePass-а значение 5 чуть выше, чем у временной покупки — 3 , то обошёлся одной переменной.
local MS = game:GetService("MarketplaceService") -- сокращение для вызова сервиса local function processReceipt(ReceiptInfo) local player = game:GetService("Players"):GetPlayerByUserId(ReceiptInfo.PlayerId) -- получаем Id игрока if not player then -- если не игрок, то вылетаем return Enum.ProductPurchaseDecision.NotProcessedYet end -- если игрок найден, выполняем действия -- print(ReceiptInfo.PlayerId .. " buy " .. ReceiptInfo.ProductId) local char=player.Character if player:FindFirstChild("Buy") == nil then -- проверяем наличие переменной, если нет - создаём local Buy = Instance.new("IntValue") Buy.Parent = player Buy.Name = "Buy" Buy.Value=3 else player.Buy.Value = 3 end char.Humanoid.WalkSpeed = 30 -- а теперь убираем Frame покупки local pl = game.Players:FindFirstChild(player.Name) if pl ~= nil then pl.PlayerGui.Main.GameDev.Visible=false pl.PlayerGui.Main.Answer.Visible=true end -- возвращаем, что покупка произведена return Enum.ProductPurchaseDecision.PurchaseGranted end MS.ProcessReceipt= processReceipt
Как видим, всё оказалось не так уж и страшно, если знать что искать.
Остальные скрипты (для кнопок) остались без изменений.
И как всегда, результат можно увидеть и опробовать на странице арифметического тренажёра:
https://www.roblox.com/games/4789977549/Math-Rocket