При първото създаване на моята игра, RoMurder Mystery, я настроих да се върти около инстанции (обекти като части, модели, boolvalues ​​и т.н.) и StringValues. Те се използват главно за идентифициране на ролите на играчите - независимо дали са Убиецът, Шерифът или Невинният. Първият ми опит да създам система за роли на играч включи стойности на обект (StringValues, IntValues ​​и BoolValues) и проработи! Доколкото можах да преценя, нямаше проблеми с начина, по който функционира... но след това разбрах, че всеки може да види тези стойности, тъй като те са родителски на играча. Което означава, че ако някой със злонамерени намерения се присъедини към играта, може да разкрие ролите на всеки играч и да разруши мистерията, основна механика на играта.

Тази ситуация е известна също като злоупотреба с екземпляри, т.е. екземпляри, които са злоупотребени от разработчика. Неправилно използвани екземпляри са екземпляри (обикновено стойности на обекти), които са изложени, вместо да бъдат скрити от всички.

В много случаи може да искате да покажете стойност - например, наличието на стойност, която идентифицира дали има наличен парцел земя, не е случай на злоупотреба. В моя случай обаче тайните роли на играчи, които се виждат от всички, е злоупотреба с инстанции, тъй като една мистериозна игра с убийства се върти около тези ценности, които остават скрити. Поради това трябваше да се потопя по-дълбоко в бекенд програмирането, за да изградя система, в която данните се създават на сървъра, остават на сървъра и разкриват само конкретни данни на играча… не на всеки. Например определяне на ролите на всеки играч на сървъра, съхраняване на тези данни в речник (таблица, която ви позволява да задавате стойности, повече за това по-долу) и споделяне на информация, която се отнася до играча , в моя случай, тяхната роля.

Бекенд програмирането е необходимост от всички програмисти да се научат, тъй като ви позволява да имате пълен контрол върху вашата игра — от играча, който взаимодейства с играта (фронтенд) до сървъра, който изпълнява всичко (бекенд ). Но първо ще трябва да определите как искате вашата игра да функционира. Моля, имайте предвид, че ако току-що започвате с Roblox Studio, съсредоточете се върху изучаването и разбирането на Lua и не се притеснявайте толкова много за бекенд данните. Backend програмирането ви позволява да съхранявате стойности на сървъра и да четете от тези стойности, спрямо които вашият код ще реши какво да прави по-нататък.

Например, да кажем, че играчът иска да купи ябълка и ябълката струва 10 долара (скъпа ябълка, знам). Разглеждайки това от гледна точка на програмиране на бекенда, първо ще искате да проверите: Играчът има ли 10 долара или повече? И също така, може ли играчът да купи това? Трябва да настроите система, която потвърждава, че играчът може да купи ябълката, приспада цената и я добавя към техния инвентар. Ако просто се придържате към „Може ли играчът да купи това?“ вероятно ще причини пропуски във вашия код и ще има нужда от допълнителни проверки.

След като определите дали могат да купят ябълката, след това се погрижете в задната част колко пари са загубени от покупката и добавете, че те притежават ябълката. Оттам, в предния край, споделете, че са купили ябълката, тя е в техния инвентар и ето остатъчната им сума в долари. Това е пример за frontend срещу backend кодиране. В бекенда сървърът определя дали всичко е вярно и споделя на играча (фронтенда) получените данни. Ако управлявате всичко във фронтенда, играчът може да манипулира системата в състояние, в което може да купи ябълката, когато в действителност не може.

Ще ви покажа сравнение на кода на данни от бекенда и данни, базирани на екземпляри. Това, което прави този код, е да създаде стойност „Cash“, но има два различни начина за това:

Настройка на задните данни:

--Backend Data Setup
 
local Players = game:GetService("Players")
 
local PlayerData = {}
 
Players.PlayerAdded:Connect(function(player)    
    local PlayerJoinedTable = PlayerData[player.Name] or {};
    PlayerData[player.Name] = PlayerJoinedData --Creates a new table (for the player's data) within the PlayerData table
 
    PlayerJoinedTable["Cash"] = 0
end)

Настройка на данни, базирана на екземпляр:

-- Instance based Data Setup
 
local Players = game:GetService("Players")
 
Players.PlayerAdded:Connect(function(player)    
    local CashIntValue = Instance.new("IntValue")
    CashIntValue.Name = "CashIntValue"
    CashIntValue.Value = 0
    CashIntValue.Parent = player -- Creating the Cash value for the player using IntValue
end)

Нека сравним двата кодови блока. И двата блока за кодиране създават система за съхраняване и създаване на стойност „Кеш“.

Настройката на данните в задната част създава речник на данните на играча и ни позволява да добавяме различни неща, които да съхраняваме в този речник (като валута). В нашия случай ние създаваме стойността на валутата „Cash“ и я задаваме на 0. Кодът, базиран на екземпляр, създава екземпляр, IntValue, като го наименува на „CashIntValue ” и задаване на стойност на 0. След това, като родител на играча.

Тези два кодови блока правят едно и също нещо, но задната система е много по-ефективна, защото можете да съхранявате много повече данни в речника в сравнение със случаите, в които трябва да създадете няколко различни екземпляра за различни стойности, които искате да създадете. Кодът на екземпляра може да стане много дълъг в зависимост от количеството данни, които искате да имате за играча. В моя случай, когато използвах екземпляри, това заемаше минимум 50 реда, докато използването на речници за данни отне само 10 реда.

Нека да видим как би изглеждал кодът, ако исках да купя ябълка. В този пример играчът има 15 долара, които да похарчи за ябълки, които струват 10 всяка. Дистанционна функция се задейства от клиента за закупуване на артикула:

Покупка в задния план:

-- Backend Shop
 
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 
local ShopOptions = {
    Apples = 10
}
 
ReplicatedStorage.BuyItem.OnServerInvoke = function(player, ItemName)
    local CustomerData = PlayerData[player.Name]
 
    if CustomerData["Cash"] >= ShopOptions[ItemName] then
         CustomerData["Cash"] -= ShopOptions[ItemName] -- Player will have 5 dollars left
 end
end

Покупка, базирана на екземпляр:
(Да приемем, че има IntValue в ReplicatedStorage за артикули от магазина)

-- Instance based shop
 
local ReplicatedStorage = game:GetService("ReplicatedStorage")
 
ReplicatedStorage.BuyItem.OnServerInvoke = function(player, ItemName)
    local ItemData = ReplicatedStorage:FindFirstChild(ItemName)
    local PlayerCash = player.CashIntValue
 
    if PlayerCash.Value >= ItemData.Value then
        PlayerCash.Value -= ItemData.Value -- Player will have 5 dollars left
    end
end

Може да забележите, че в края на деня тези два кодови блока постигат едно и също нещо, но изглеждат напълно различно.

Бекенд кодът има речник, ShopOptions, с всички разходи за всеки артикул в магазина. Първо проверяваме дали играчът има по-голяма или равна на цената на артикула в магазина, ако го направим, след това правим покупката и изваждаме цената на артикула от парите на играча.

Кодът, базиран на екземпляр, търси Apple IntValue в ReplicatedStorage, след което проверява дали играчът има по-голяма или равна на цената на артикула от магазина. Ако го направим, тогава правим покупката и изваждаме цената на артикула от парите на играча IntValue. Трябваше да направим допълнителни стъпки в базираното на екземпляр кодиране, защото трябваше да намерим Apples IntValue, докато с помощта на речник можем просто да включим ItemName в речника на ShopOptions и бързо вземете стойността.

Въпросът, който имате предвид, е каква е разликата между тези два различни метода, ако в крайна сметка имат същия резултат? Backend кодирането е много по-чисто, много по-лесно е да се идентифицират грешки (ако има такива). Например грешка, която гласи, че имате грешка на ред x, защото сте забравили запетая или стойността е нула и поради по-краткия брой редове ще бъде много по-лесно да се коригира. По-важното е, че само вие ще виждате тези стойности, никой друг не може да види или да поиска информацията (освен ако не напишете кода, за да го направите).

Речниците се използват широко и в други кодиращи езици (Python, C++, SQL и др.). Научаването как да използвате този вид система ще ви даде бърз старт, когато започнете да кодирате с помощта на тези големи системи. Искам да отбележа, че има някои случаи, в които може да се наложи да използвате екземпляри, но не забравяйте: злоупотребата им може да бъде проблематична, защото може да влоши изживяването на играта, която сте проектирали, ако някой знае нещо те не трябва да знаят.

Двигателите, като Roblox Studio, ви позволяват да вдъхнете живот на идеите за игри чрез програмиране на специфични функции, като например да имате обекти, които да се държат по определен начин. Реализирането на една идея не винаги означава да я направите по възможно най-ефективния начин. Откривам, че с всяко ново нещо, което научавам, поглеждам назад към стари проекти и откривам нови, по-ефективни начини да постигна това, което първоначално съм си поставил за цел да създам.

Здравейте! Аз съм WooleyWool, организатор на събития в общността на Roblox и програмист. Превръщане на идеите за игри в реалност, ред по ред.

Научих много от първоначалните си познания по програмиране от университета Roblox и топ програмисти в YouTube. Наскоро се заех с мисията да създам своя собствена игра! RoMurder Mystery е моя собствена представа за игра в стил мистериозни убийства — създаването ѝ ме научи на много повече за програмирането в Roblox от това, което знаех, когато за първи път започнах.

Бъдете в крак с мен в Twitter

Присъединете се към милионите творци в Roblox и направете първото си изживяване с помощта на тази страница за първи стъпки.