Во многих играх используются показатели игрока для отслеживания его игровых данных с течением времени. Такие метрики, как счёт, количество выигранных игр, общее время в игре и собранные предметы, дают игрокам ощущение прогресса и являются отличным способом побудить игроков возвращаться в ваш мир снова и снова.
Verse Persistence — это мощный инструмент, позволяющий добавлять сохраняемые данные в ваш сценарий Verse. Данные могут сохраняться как для каждого отдельного игрока, так и для всего острова после выхода из одного сеанса игры и до входа в другой. Сохраняемые данные позволяют отслеживать прогресс игрока от сеанса к сеансу и открывают возможность реализовать множество уникальных и интересных жанров, недоступных ранее в UEFN.
В этом уроке вы узнаете, как создать пользовательскую таблицу статистики игрока на Verse и настроить её так, чтобы она сохранялась на протяжении нескольких игр. После завершения этого урока загляните в раздел Создание внутриигровой таблицы лидеров на Verse, чтобы узнать, как использовать сохраняемые данные для создания внутриигровой таблицы лидеров!
Используемые функции языка Verse
-
Класс: в этом примере мы создадим класс Verse, который будет управлять отдельным показателем, а также сохраняемый класс, отслеживающий группу показателей одного игрока.
-
Конструктор: это специальная функция, которая создаёт экземпляр связанного с ней класса.
-
weak_map: это простой слабый ассоциативный массив, итерация по которому не производится. Сохраняемые данные Verse должны храниться в weak_map.
Настройка уровня
В этом примере используются следующие объекты окружения и устройства.
-
2 устройства кнопки. Когда игрок взаимодействует с устройством, к его счёту прибавляется одно очко. С помощью другого устройства кнопки вы будете имитировать окончание игры, добавляя выигрыш или проигрыш игрока в зависимости от его текущего счета.
-
1 устройство «Рекламный щит». Часто бывает нужно показать сохраняемые данные игроку. Иногда это делается в рамках тестирования, а иногда — чтобы сильнее вовлечь игрока или показать его прогресс. Хотя требования к тому, когда и какие данные показывать, варьируются в зависимости от игры, в данном примере на рекламном щите будут показаны данные о счёте, рекорде, победах и поражениях.

Отслеживание сохраняемой статистики игрока
Сначала важно определить, какие показатели нужно отслеживать для каждого игрока. К примеру, может потребоваться отслеживать количество очков игрока за все время игры, его текущий рейтинг или его лучшее время на круге. В этом примере мы будем отслеживать счёт, победы и поражения в таблице значений показателей для каждого игрока. Всё это будет в новом классе player_stats_table
, который мы будем использовать в качестве основного сохраняемого класса.
Для создания класса player_stats_table
выполните следующие шаги:
-
Создайте новый файл Verse с помощью проводника Verse под названием
player_stats_table.verse
. -
Создайте в этом файле Verse новый класс
player_stats_table
. Добавьте в класс модификаторы<persistable>
и<final>
. Модификатор<persistable>
позволяет сделать данные в классе сохраняемыми и должен дополняться модификатором<final>
, потому что сохраняемые данные нельзя переопределить или создать из них подкласс.using { /Fortnite.com/Devices } using { /Verse.org/Simulation } using { /UnrealEngine.com/Temporary/Diagnostics } # Отслеживает различные сохраняемые показатели каждого игрока. player_stats_table := class<final><persistable>:
-
В
player_stats_table
добавьте три значения типаint
с названиямиScore
,Wins
иLosses
. С их помощью мы отслеживаем счёт за всё время, а также количество побед и поражений для каждого отдельного игрока. Также добавьте значение типаint
с названиемVersion
, чтобы отслеживать текущую версиюplayer_stats_table
.# Отслеживает различные сохраняемые показатели каждого игрока. player_stats_table := class<final><persistable>: # Версия текущей таблицы статистики. Version<public>:int = 0 # Это счёт игрока. Score<public>:int = 0 # Количество побед игрока. Wins<public>:int = 0 # Количество поражений игрока. Losses<public>:int = 0
-
Для создания экземпляра класса
player_stats_table
мы будем использовать функцию<constructor>
. Этот конструктор необходим, поскольку алгоритм сохранения Verse не позволяет создавать сохраняемые классы с полями переменных. При помощи конструктора мы сможем обновлять значения сохраняемого класса путём создания копии существующего сохраняемого показателя, который служит переменной, обновлять копию, а затем заменять исходный экземпляр класса изменёнными значениями. Добавьте новую функцию-конструкторMakePlayerStatsTable()
в свой файл. Этот конструктор будет принимать исходный (предыдущий) экземпляр классаplayer_stats_table
и создавать новый на основе исходных заданных значений.# Создаёт новую таблицу player_stats_table с теми же значениями, что и в предыдущей. MakePlayerStatsTable<constructor>(OldTable:player_stats_table)<transacts> := player_stats_table: Version := OldTable.Version Score := OldTable.Score Wins := OldTable.Wins Losses := OldTable.Losses
-
Для отслеживания всех
player_stats_tables
мы будем использовать сохраняемый ассоциативный массивweak_map
из экземпляраplayer
в экземплярplayer_stats_table
. Добавьте этот слабый ассоциативный массив в свой файл.# Сопоставляет игроков с таблицей их показателей. var PlayerStatsMap:weak_map(player, player_stats_table) = map{}
-
Готовый класс
player_stats_table
должен выглядеть следующим образом:using { /Fortnite.com/Devices } using { /Verse.org/Simulation } using { /UnrealEngine.com/Temporary/Diagnostics } # Отслеживает различные сохраняемые показатели каждого игрока. player_stats_table := class<final><persistable>: # Версия текущей таблицы статистики. Version<public>:int = 0 # Это счёт игрока. Score<public>:int = 0 # Количество побед игрока. Wins<public>:int = 0 # Количество поражений игрока. Losses<public>:int = 0 # Создаёт новую таблицу player_stats_table с теми же значениями, что и в предыдущей. MakePlayerStatsTable<constructor>(OldTable:player_stats_table)<transacts> := player_stats_table: Version := OldTable.Version Score := OldTable.Score Wins := OldTable.Wins Losses := OldTable.Losses # Сопоставляет игроков с таблицей их показателей. var PlayerStatsMap:weak_map(player, player_stats_table) = map{}
Управление показателями для каждого игрока
Касс player_stats_table
позволяет отслеживать показатели отдельного игрока, но пока мы не можем управлять ими. Нужно обновлять таблицы статистики для каждого игрока всякий раз, когда он получает очки, а в зависимости от дизайна вашей игры может одновременно отображаться много игроков.
Для этого вы будете использовать другой класс, который управляет статистикой всех игроков и записывает изменения показателей каждый раз, когда игрок одерживает победу, проигрывает или набирает очки. Чтобы настроить управляющий класс, выполните следующие действия:
-
Создайте новый файл Verse с именем
player_stats_manager
с помощью Проводника Verse. Создайте в этом файле новый классplayer_stats_manager
.using { /Fortnite.com/Devices } using { /Verse.org/Simulation } using { /UnrealEngine.com/Temporary/Diagnostics } # Управляет таблицами player_stat_table для каждого игрока и обновляет их. player_stats_manager := class():
-
Файл
player_stats_manager
должен делать несколько вещей. Нужно настроитьplayer_stats_table
для игрока, обновлять значенияScore
,Wins
иLosses
каждого игрока и возвращать игроку значенияplayer_stats_table
. Для каждого из этих действий прописывается отдельная функция. Добавьте новую функциюInitializePlayer()
в определение классаplayer_stats_manager
. Эта функция будет инициализировать показатели заданного игрока.# Инициализируем показатели заданного игрока. InitializePlayer(Player:player):void=
-
В
InitializePlayer()
проверьте, существует ли уже заданный игрок вPlayerStatsMap
. Если нет, задайте значение этого игрока в массиве для новой таблицыplayer_stats_table
. Готовая функцияInitializePlayer()
должна выглядеть следующим образом:# Инициализируем показатели заданного игрока. InitializePlayer(Player:player):void= if: not PlayerStatsMap[Player] set PlayerStatsMap[Player] = player_stats_table{} else: Print("Невозможно инициализировать статистику игрока")
-
Добавьте новую функцию
InitializeAllPlayers()
в определение классаplayer_stats_manager
. Эта функция содержит массив игроков и вызываетInitializePlayer()
для каждого из них. Готовая функцияInitializeAllPlayers()
должна выглядеть следующим образом:# Инициализируем показатели каждого текущего игрока. InitializeAllPlayers(Players:[]player):void = for (Player : Players): InitializePlayer(Player)
-
Чтобы вернуть показатели определённого игрока, вам нужна функция, которая возвращает таблицу
player_stats_table
этого игрока. Добавьте в определение классаplayer_stats_manager
новую функциюGetPlayerStats()
, которая содержит значение агента. Добавьте модификатор<decides><transacts>
, позволяющий этой функции возвращать неоднозначный результат и откатываться назад в случае, если таблицы статистики игрока не существует. ВGetPlayerStats()
создайте новую переменнуюPlayerStats
для таблицыplayer_stats_table
.# Возвращаем player_stats_table для предоставленного агента. GetPlayerStats(Agent:agent)<decides><transacts>:player_stats_table= var PlayerStats:player_stats_table = player_stats_table{}
-
В выражении
if
приведите типAgent
, передаваемый через эту функцию, к типуPlayer
, а затем получите таблицуplayer_stats_table
для этого игрока изPlayerStatsMap
. Затем задайтеPlayerStats
для этой таблицы, вызвавMakePlayerStatsTable()
. В конечном итоге мы получимPlayerStats
. ФункцияGetPlayerStats()
в конечном итоге должна выглядеть следующим образом:# Возвращаем player_stats_table для предоставленного агента. GetPlayerStats(Agent:agent)<decides><transacts>:player_stats_table= var PlayerStats:player_stats_table = player_stats_table{} if: Player := player[Agent] PlayerStatsTable := PlayerStatsMap[Player] set PlayerStats = MakePlayerStatsTable(PlayerStatsTable) PlayerStats
-
Для обновления показателей счёта, побед и поражений мы создадим отдельные функции для каждого из них. Добавьте новую функцию
AddScore()
в файлplayer_stats_manager
. Эта функция принимает агент для начисления очков и количество начисляемых очков (типаint
).# Добавляется к победам заданного агента и обновляет таблицу для обоих показателей # в PlayerStatsManager и на рекламном щите на уровне. AddScore<public>(Agent:agent, NewScore:int):void=
-
Данные обновляются следующим образом: сначала выполняется проверка, что данные в сохраняемом массиве
weak_map
являются допустимыми, после чего эти данные заменяются на обновлённую копию класса. Чтобы выполнить эту процедуру для счёта, мы получаем счёт игрока изPlayerStatsTable
, затем для таблицы вPlayerStatsMap
задаём результат создания новой таблицыplayer_stats_table
с помощьюMakePlayerStatsTable()
, передавая значение текущего счёта с добавлением нового счёта. При работе с классом, содержащим несколько полей, конструктор класса позволяет легко обновить отдельное поле без необходимости каждый раз копировать все поля, когда необходимо выполнить обновление. ФункцияAddScore()
в конечном итоге должна выглядеть следующим образом:# Добавляется к победам заданного агента и обновляет таблицу для обоих показателей # в PlayerStatsManager и на рекламном щите на уровне. AddScore<public>(Agent:agent, NewScore:int):void= if: Player := player[Agent] PlayerStatsTable := PlayerStatsMap[Player] CurrentScore := PlayerStatsTable.Score set PlayerStatsMap[Player] = player_stats_table: MakePlayerStatsTable<constructor>(PlayerStatsTable) Score := CurrentScore + NewScore else: Print("Невозможно записать счёт игрока")
-
Повторите те же шаги для показателей побед и поражений, добавив
NewWins
иNewLosses
к победам или поражениям игрока соответственно при вызовеMakePlayerStatsTable()
.# Добавляется к победам заданного агента и обновляет таблицу для обоих показателей # в PlayerStatsManager и на рекламном щите на уровне. AddWin<public>(Agent:agent, NewWins:int):void= if: Player := player[Agent] PlayerStatsTable := PlayerStatsMap[Player] CurrentWins := PlayerStatsTable.Wins set PlayerStatsMap[Player] = player_stats_table: MakePlayerStatsTable<constructor>(PlayerStatsTable) Wins := CurrentWins + NewWins else: Print("Невозможно записать победы игрока") # Добавляется к поражениям заданного агента и обновляет таблицу для обоих показателей # в PlayerStatsManager и на рекламном щите на уровне. AddLoss<public>(Agent:agent, NewLosses:int):void= if: Player := player[Agent] PlayerStatsTable := PlayerStatsMap[Player] CurrentLosses := PlayerStatsTable.Losses set PlayerStatsMap[Player] = player_stats_table: MakePlayerStatsTable<constructor>(PlayerStatsTable) Losses := CurrentLosses + NewLosses else: Print("Невозможно записать поражение игрока")
-
Последний файл
player_stats_manager
должен выглядеть следующим образом.using { /Fortnite.com/Devices } using { /Verse.org/Simulation } using { /UnrealEngine.com/Temporary/Diagnostics } # Управляет таблицами player_stat_table для каждого игрока и обновляет их. player_stats_manager := class(): # Возвращаем player_stats_table для предоставленного агента. GetPlayerStats(Agent:agent)<decides><transacts>:player_stats_table= var PlayerStats:player_stats_table = player_stats_table{} if: Player := player[Agent] PlayerStatsTable := PlayerStatsMap[Player] set PlayerStats = MakePlayerStatsTable(PlayerStatsTable) PlayerStats # Инициализируем показатели каждого текущего игрока. InitializeAllPlayers(Players:[]player):void = for (Player : Players): InitializePlayer(Player) # Инициализируем показатели заданного игрока. InitializePlayer(Player:player):void= if: not PlayerStatsMap[Player] set PlayerStatsMap[Player] = player_stats_table{} else: Print("Невозможно инициализировать статистику игрока") # Добавляется к победам заданного агента и обновляет таблицу для обоих показателей # в PlayerStatsManager и на рекламном щите на уровне. AddScore<public>(Agent:agent, NewScore:int):void= if: Player := player[Agent] PlayerStatsTable := PlayerStatsMap[Player] CurrentScore := PlayerStatsTable.Score set PlayerStatsMap[Player] = player_stats_table: MakePlayerStatsTable<constructor>(PlayerStatsTable) Score := CurrentScore + NewScore else: Print("Невозможно записать счёт игрока") # Добавляется к победам заданного агента и обновляет таблицу для обоих показателей # в PlayerStatsManager и на рекламном щите на уровне. AddWin<public>(Agent:agent, NewWins:int):void= if: Player := player[Agent] PlayerStatsTable := PlayerStatsMap[Player] CurrentWins := PlayerStatsTable.Wins set PlayerStatsMap[Player] = player_stats_table: MakePlayerStatsTable<constructor>(PlayerStatsTable) Wins := CurrentWins + NewWins else: Print("Невозможно записать победы игрока") # Добавляется к поражениям заданного агента и обновляет таблицу для обоих показателей # в PlayerStatsManager и на рекламном щите на уровне. AddLoss<public>(Agent:agent, NewLosses:int):void= if: Player := player[Agent] PlayerStatsTable := PlayerStatsMap[Player] CurrentLosses := PlayerStatsTable.Losses set PlayerStatsMap[Player] = player_stats_table: MakePlayerStatsTable<constructor>(PlayerStatsTable) Losses := CurrentLosses + NewLosses else: Print("Невозможно записать поражение игрока")
Тестирование сохранения показателей с использованием устройств
Теперь, когда вы настроили сохраняемые классы, можно протестировать их на вашем уровне.
-
Создайте новое устройство Verse и назовите его player_stats_example. См. описание действий в разделе Создание собственного устройства с помощью Verse.
-
В верхних строках определения класса
player_stats_example
добавьте следующие поля:-
Редактируемое устройство
button_device
с названиемScorePointsButton
. Это кнопка при активации добавляет счёт игроку.# Увеличивает счёт активирующего игрока. @editable ScorePointsButton:button_device = button_device{}
-
Редактируемое устройство
billboard_device
с названиемStatsBillboard
. Оно показывает счёт, рекорд, количество побед и поражений игрока.# Показывает счёт, рекорд, количество побед и поражений игрока @editable StatsBillboard:billboard_device = billboard_device{}
-
Редактируемое устройство
button_device
с названиемCheckWinButton
. Эта кнопка сбрасывает счёт каждого игрока и назначает ему победу или поражение в зависимости от счёта игрока.# Сбрасывает счёт игрока и назначает ему победу или поражение # в зависимости от счёта игрока. @editable CheckWinButton:button_device = button_device{}
-
Редактируемая переменная типа
int
с названиемWinScore
. Это счёт, которого должны достичь игроки, чтобы им была засчитана победа после активации кнопкиCheckWinButton
.# Это счёт, которого должны достичь игроки, чтобы им была засчитана победа # после активации кнопки CheckWinButton. @editable WinScore:int = 10
-
Редактируемая переменная типа
int
с названиемAwardScore
. Это количество очков, которое начисляется игрокам при взаимодействии с кнопкой.# Количество очков, начисляемое за нажатие кнопки. @editable AwardScore:int = 1
-
Диспетчер статистики
player_stats_manager
с названиемPlayerStatsManager
. Он будет управлять показателями для каждого игрока и обновлять их.# Управляет показателями каждого игрока и обновляет их. PlayerStatsManager:player_stats_manager = player_stats_manager{}
-
Сообщение StatsMessage, которое принимает от агента четыре целочисленных значения: Score, MaxScore, Wins и Losses. Это сообщение будет показывать игроку его статистику на рекламном щите.
# Показывает игроку его статистику на рекламном щите. StatsMessage<localizes>(Player:agent, Score:int, Wins:int, Losses:int):message = "{Player}, Статистика:\n Счёт: {Score}\n Побед: {Wins}\n Поражений: {Losses}"
-
-
Скомпилируйте код и перетащите Verse-устройство на свой остров. См., как это сделать, в уроке Добавление Verse-устройства на свой уровень.
-
На панели Сведения устройств на вашем уровне назначьте устройству Кнопка значение ScorePointsButton, а устройству Рекламный щит — значение StatsBillboard.
-
Чтобы отобразить статистику игрока на StatsBillboard, добавьте новую функцию
UpdateStatsBillboard()
в определение классаplayer_stats_example
. Эта функция содержит агент, показатели которого нужно отобразить.# Извлекает статистику заданного игрока и выводит её # на экран StatsBillboard. UpdateStatsBillboard(Agent:agent):void=
-
Получите текущие показатели заданного агента в
UpdateStatsBillboard()
, вызвав функцию диспетчера статистикиGetPlayerStats[]
. Затем вызовитеSetText()
для StatsBillboard и передайте новое сообщениеStatsMessage()
. Для компоновки этого сообщенияStatsMessage()
нужно получить значения Score, Wins и Losses из текущих показателей агента. Готовая функцияUpdateStatsBillboard()
должна выглядеть следующим образом:# Извлекает статистику заданного игрока и выводит её # на экран StatsBillboard. UpdateStatsBillboard(Agent:agent):void= if: # Получить текущие показатели заданного агента. CurrentPlayerStats := PlayerStatsManager.GetPlayerStats[Agent] then: StatsBillboard.SetText( StatsMessage( Player := Agent, Score:=CurrentPlayerStats.Score.CurrentValue, Wins:=CurrentPlayerStats.Wins.CurrentValue, Losses:=CurrentPlayerStats.Losses.CurrentValue ) )
-
Добавьте новую функцию
AddScore()
в определение классаplayer_stats_example
. Эта функция содержит агент и добавляет ему очки при каждом взаимодействии с кнопкой ScorePointsButton.# Добавляется к счёту заданного игрока и обновляет его таблицу статистики # в PlayerStatsManager и на рекламном щите на уровне. AddScore(Agent:agent):void=
-
В
AddScore()
получаем текущую статистику заданного агента, а также его текущий счёт. Затем вызываемAddScore()
изPlayerStatsManager
и передаём агент иAwardScore
, чтобы присвоить ему новый счёт. И наконец, вызываемUpdateStatsBillboard()
и передаём заданный агент. Готовая функцияAddScore()
должна выглядеть следующим образом:# Добавляется к счёту заданного игрока и обновляет его таблицу статистики # в PlayerStatsManager и на рекламном щите на уровне. AddScore(Agent:agent):void= if: CurrentPlayerStats := PlayerStatsManager.GetPlayerStats[Agent] CurrentScore := CurrentPlayerStats.Score then: Print("Текущий счёт: {CurrentScore}") PlayerStatsManager.AddScore(Agent, AwardScore) UpdateStatsBillboard(Agent)
-
Чтобы присуждать игроку выигрыш или проигрыш при нажатии на кнопку CheckWin, добавьте новую функцию
CheckWin()
в определение классаplayer_stats_manager
.# Присудить игроку победу или поражение при взаимодействии # с кнопкой CheckWinButton. CheckWin(Agent:agent):void=
-
Сначала задайте переменную
CurrentScore
для отслеживания текущего счёта агента. Затем, как и в случае с функциейAddScore()
, получите текущий счёт из таблицы статистики игрока.# Присудить игроку победу или поражение при взаимодействии # с кнопкой CheckWinButton. CheckWin(Agent:agent):void= var CurrentScore:int = 0 if: PlayerStats := PlayerStatsManager.GetPlayerStats[Agent] set CurrentScore = PlayerStats.Score
-
Если текущий счёт агента превышает значение
WinScore
, нужно записать победу вPlayerStatsManager
, в противном случае следует записать поражение. Наконец, сбросьте счёт агента, вызвавAddScore()
с отрицательнымCurrentScore
, а затем отобразите его статистику на щите статистики. В конечном итоге функцияCheckWin()
должна выглядеть следующим образом:# Присудить игроку победу или поражение при взаимодействии # с кнопкой CheckWinButton. CheckWin(Agent:agent):void= var CurrentScore:int = 0 if: PlayerStats := PlayerStatsManager.GetPlayerStats[Agent] set CurrentScore = PlayerStats.Score then: Print("Текущий счёт: {CurrentScore}") if: CurrentScore > WinScore then: PlayerStatsManager.AddWin(Agent, 1) # Сбрасываем счёт игрока, записав показатель счёта как 0. PlayerStatsManager.AddScore(Agent, -CurrentScore) else: PlayerStatsManager.AddLoss(Agent, 1) # Сбрасываем счёт игрока, записав показатель счёта как 0. PlayerStatsManager.AddScore(Agent, -CurrentScore) UpdateStatsBillboard(Agent)
-
Подписываем
ScorePointsButton.InteractedWithEvent
наAddScore()
, аCheckWinButton.InteractedWithEvent
наCheckWin()
вOnBegin()
. Затем получаем массив каждого игрока в игре, вызвавGetPlayers()
, и инициализируем их всех с помощью функции диспетчера статистикиInitializeAllPlayers()
.# Выполняется при запуске устройства в работающей игре OnBegin<override>()<suspends>:void= # События кнопки регистрации ScorePointsButton.InteractedWithEvent.Subscribe(AddScore) CheckWinButton.InteractedWithEvent.Subscribe(CheckWin) Players := GetPlayspace().GetPlayers() # Инициализировать показатели игроков PlayerStatsManager.InitializeAllPlayers(Players)
-
Сохраните свой код и скомпилируйте его.
Тестирование сохраняемых данных в игре
Вы можете протестировать сохраняемые данные в сеансе редактирования, но они будут сброшены при выходе из сеанса и его перезапуске. Чтобы данные сохранялись от сеанса к сеансу, нужно запустить сеанс тестирования и изменить некоторые настройки в разделе Настройки острова. Более подробную информацию о том, как тестировать сохраняемые данные в редакторе и в игре см. в разделе Тестирование с сохраняемыми данными на странице о сохраняемых данных.
После настройки сеанса нажатие кнопки ScorePoints во время игрового теста должно добавлять игроку очки и отображать это обновление на рекламном щите. Взаимодействие с кнопкой CheckWin должно увеличивать значение побед или поражений игрока, в зависимости от счёта игрока. После возвращения в лобби и повторного входа на остров статистика игрока должна сохраниться, а значения его побед/поражений и рекорд будут отображаться на рекламном щите при каждом его обновлении.

Самостоятельная работа
В этом уроке вы узнали, как использовать Verse для создания сохраняемых данных, отслеживаемых для каждого игрока от сеанса к сеансу. А теперь попробуйте придумать, как можно использовать сохраняемые данные, чтобы улучшить свою игру.
- Получится ли у вас создать систему сохранения, которая запоминала бы последнюю контрольную точку, до которой добрался игрок?
- Как насчет системы, которая запоминает, с какими персонажами вы уже общались, и ваши текущие отношения с ними?
- Может быть можно создать систему, которая даёт игрокам ограниченное количество времени на достижение целей, а в случае неудачи сбрасывает их прогресс?
Закончить код
Ниже приведён полный код, написанный в этом разделе обучения.
player_stats_table.verse
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
# Отслеживает различные сохраняемые показатели каждого игрока.
player_stats_table := class<final><persistable>:
# Версия текущей таблицы статистики.
Version<public>:int = 0
# Это счёт игрока.
Score<public>:int = 0
# Количество побед игрока.
Wins<public>:int = 0
# Количество поражений игрока.
Losses<public>:int = 0
# Создаёт новую таблицу player_stats_table с теми же значениями, что и в предыдущей.
MakePlayerStatsTable<constructor>(OldTable:player_stats_table)<transacts> := player_stats_table:
Version := OldTable.Version
Score := OldTable.Score
Wins := OldTable.Wins
Losses := OldTable.Losses
# Сопоставляет игроков с таблицей их показателей.
var PlayerStatsMap:weak_map(player, player_stats_table) = map{}
player_stats_manager.verse
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
# Управляет таблицами player_stat_table для каждого игрока и обновляет их.
player_stats_manager := class():
# Возвращаем player_stats_table для предоставленного агента.
GetPlayerStats(Agent:agent)<decides><transacts>:player_stats_table=
var PlayerStats:player_stats_table = player_stats_table{}
if:
Player := player[Agent]
PlayerStatsTable := PlayerStatsMap[Player]
set PlayerStats = MakePlayerStatsTable(PlayerStatsTable)
PlayerStats
# Инициализируем показатели каждого текущего игрока.
InitializeAllPlayers(Players:[]player):void =
for (Player : Players):
InitializePlayer(Player)
# Инициализируем показатели заданного игрока.
InitializePlayer(Player:player):void=
if:
not PlayerStatsMap[Player]
set PlayerStatsMap[Player] = player_stats_table{}
else:
Print("Невозможно инициализировать статистику игрока")
# Добавляется к победам заданного агента и обновляет таблицу для обоих показателей
# в PlayerStatsManager и на рекламном щите на уровне.
AddScore<public>(Agent:agent, NewScore:int):void=
if:
Player := player[Agent]
PlayerStatsTable := PlayerStatsMap[Player]
CurrentScore := PlayerStatsTable.Score
set PlayerStatsMap[Player] = player_stats_table:
MakePlayerStatsTable<constructor>(PlayerStatsTable)
Score := CurrentScore + NewScore
else:
Print("Невозможно записать счёт игрока")
# Добавляется к победам заданного агента и обновляет таблицу для обоих показателей
# в PlayerStatsManager и на рекламном щите на уровне.
AddWin<public>(Agent:agent, NewWins:int):void=
if:
Player := player[Agent]
PlayerStatsTable := PlayerStatsMap[Player]
CurrentWins := PlayerStatsTable.Wins
set PlayerStatsMap[Player] = player_stats_table:
MakePlayerStatsTable<constructor>(PlayerStatsTable)
Wins := CurrentWins + NewWins
else:
Print("Невозможно записать победы игрока")
# Добавляется к поражениям заданного агента и обновляет таблицу для обоих показателей
# в PlayerStatsManager и на рекламном щите на уровне.
AddLoss<public>(Agent:agent, NewLosses:int):void=
if:
Player := player[Agent]
PlayerStatsTable := PlayerStatsMap[Player]
CurrentLosses := PlayerStatsTable.Losses
set PlayerStatsMap[Player] = player_stats_table:
MakePlayerStatsTable<constructor>(PlayerStatsTable)
Losses := CurrentLosses + NewLosses
else:
Print("Невозможно записать поражение игрока")
player_stats_example.verse
using { /Fortnite.com/Devices }
using { /Fortnite.com/Game }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
# Это Verse-устройство творческого режима, которое можно разместить на уровне
player_stats_example := class(creative_device):
# Увеличивает счёт активирующего игрока.
@editable
ScorePointsButton:button_device = button_device{}
# Показывает счёт, рекорд, количество побед и поражений игрока
@editable
StatsBillboard:billboard_device = billboard_device{}
# Сбрасывает счёт игрока и назначает ему победу или поражение
# в зависимости от счёта игрока.
@editable
CheckWinButton:button_device = button_device{}
# Это счёт, которого должны достичь игроки, чтобы им была засчитана победа
# после активации кнопки CheckWinButton.
@editable
WinScore:int = 10
# Количество очков, начисляемое за нажатие кнопки.
@editable
AwardScore:int = 1
# Управляет показателями каждого игрока и обновляет их.
PlayerStatsManager:player_stats_manager = player_stats_manager{}
# Показывает игроку его статистику на рекламном щите.
StatsMessage<localizes>(Player:agent, Score:int, Wins:int, Losses:int):message = "{Player}, Статистика:\n Счёт: {Score}\n Побед: {Wins}\n Поражений: {Losses}"
# Выполняется при запуске устройства в работающей игре
OnBegin<override>()<suspends>:void=
# События кнопки регистрации
ScorePointsButton.InteractedWithEvent.Subscribe(AddScore)
CheckWinButton.InteractedWithEvent.Subscribe(CheckWin)
Players := GetPlayspace().GetPlayers()
# Инициализировать показатели игроков
PlayerStatsManager.InitializeAllPlayers(Players)
# Добавляется к счёту заданного игрока и обновляет его таблицу статистики
# в PlayerStatsManager и на рекламном щите на уровне.
AddScore(Agent:agent):void=
if:
CurrentPlayerStats := PlayerStatsManager.GetPlayerStats[Agent]
CurrentScore := CurrentPlayerStats.Score
then:
Print("Текущий счёт: {CurrentScore}")
PlayerStatsManager.AddScore(Agent, AwardScore)
UpdateStatsBillboard(Agent)
# Извлекает статистику заданного игрока и выводит её
# на экран StatsBillboard.
UpdateStatsBillboard(Agent:agent):void=
if:
# Получить текущие показатели заданного агента.
CurrentPlayerStats := PlayerStatsManager.GetPlayerStats[Agent]
then:
StatsBillboard.SetText(
StatsMessage(
Player := Agent,
Score:=CurrentPlayerStats.Score,
Wins:=CurrentPlayerStats.Wins,
Losses:=CurrentPlayerStats.Losses
)
)
# Присудить игроку победу или поражение при взаимодействии
# с кнопкой CheckWinButton.
CheckWin(Agent:agent):void=
var CurrentScore:int = 0
if:
PlayerStats := PlayerStatsManager.GetPlayerStats[Agent]
set CurrentScore = PlayerStats.Score
then:
Print("Текущий счёт: {CurrentScore}")
if:
CurrentScore > WinScore
then:
PlayerStatsManager.AddWin(Agent, 1)
# Сбрасываем счёт игрока, записав показатель счёта как 0.
PlayerStatsManager.AddScore(Agent, -CurrentScore)
else:
PlayerStatsManager.AddLoss(Agent, 1)
# Сбрасываем счёт игрока, записав показатель счёта как 0.
PlayerStatsManager.AddScore(Agent, -CurrentScore)
UpdateStatsBillboard(Agent)