При завершении этого шага в игре Гонка на время: Доставка пиццы вы узнаете, как управлять количеством заработанных очков, когда игрок подбирает предметы и доставляет их, а также как обновлять пользовательский интерфейс для отображения этих очков. Процесс создания внутриигрового пользовательского интерфейса при помощи Verse более подробно описан в разделе Создание внутриигрового интерфейса.
Панель управления счётом будет отслеживать и отображать следующее:
Всего очков: отображает общее количество очков, которые игрок заработал в игре.
Набрано очков: отображает очки, которые игрок набрал за текущую серию подборов.
Уровень подбора: отображает текущий уровень подбора.
Создание интерфейса
Ниже пошагово описан процесс создания интерфейса для панели управления счётом в Verse:
Создайте пустой файл Verse и назовите его score_manager.verse.
Создайте новый класс
score_managerи добавьте к нему следующие поля:Константу
MaybePlayerтипаagent, которая может не содержать значение, для хранения ссылки на игрока.MaybePlayer<internal> : ?agent = falseКонстанту
MaybePlayerUIтипаplayer_ui, которая может не содержать значение, для хранения ссылки на интерфейс игрока.MaybePlayerUI<internal> : ?player_ui = falseКласс
score_manager_deviceдля хранения ссылки на устройство «Панель управления счётом», вокруг которого и строится этот класс. (Обратите внимание: это не редактируемая ссылка, потому что она должна быть связана с Verse-устройствомgame_coordinator_device.ScoreManagerDevice<internal> : score_manager_device = score_manager_device{}Целочисленную переменную
TotalGameScore, в которой будут храниться все очки, которые заработал игрок в игре за всё время.var TotalGameScore<private> : int = 0Целочисленную переменную
PendingScore, в которой будет храниться количество очков, которое игрок получил за текущую серию подборов.var PendingScore<private> : int = 0Целочисленную переменную
PickupLevel, в которой будет храниться текущий уровень подбора.var PickupLevel<private> : int = 0
Ваш класс score_manager должен выглядеть следующим образом:
score_manager := class: MaybePlayer<internal> : ?agent = false MaybePlayerUI<internal> : ?player_ui = false ScoreManagerDevice<internal> : score_manager_device = score_manager_device{} var TotalGameScore<private> : int = 0 var PendingScore<private> : int = 0 var PickupLevel<private> : int = 0Создайте интерфейс при первом создании класса. Это можно сделать, добавив в определение класса выражение с
block, которое будет выполняться всякий раз, когда вы создаёте экземпляр класса. Добавьте следующие переменные для генерации интерфейса:Переменную
Canvasтипаcanvasс внутренним спецификаторомinternalдля хранения пользовательского виджета рабочей области.var Canvas<internal> : canvas = canvas{}Константу
TotalGameScoreWidgetтипаtext_blockс внутренним спецификаторомinternalдля хранения текстового виджета, отображающего все набранные игроком очки (представлено переменнойTotalGameScore). Для текстового блока установите белый цвет текста по умолчанию.TotalGameScoreWidget<internal> : text_block = text_block{DefaultTextColor := NamedColors.White}Класс
PendingScoreWidgetтипаtext_blockсо спецификаторомinternalдля хранения текстового виджета для отображения очков, которые игрок набрал за текущую серию подборов (представлено переменнойPendingScore). Для текстового блока установите белый цвет текста по умолчанию.PendingScoreWidget<internal> : text_block = text_block{}Класс
PickupLevelWidgetтипаtext_blockсо спецификаторомinternalдля хранения текстового виджета для отображения текущего уровня подбора (представлено переменнойPickupLevel). Для текстового блока установите белый цвет текста по умолчанию.PickupLevelWidget<internal> : text_block = text_block{}Функцию, возвращающую объект
TotalGameScoreTextклассаmessageи создающую локализуемый текст с общим количеством очков, набранных игроком в игре, который может быть отображён в интерфейсе.TotalGameScoreText<localizes>(CurrentTotalGameScore : int) : message = "Всего очков: {CurrentTotalGameScore}"Функцию, возвращающую объект
PendingScoreTextклассаmessageи создающую локализуемый текст с количеством очков, набранных игроком за текущую серию подборов, который может быть отображён в интерфейсе.PendingScoreText<localizes>(CurrentPendingScore : int) : message = "Набрано очков: {CurrentPendingScore}"Функцию, возвращающую объект
PickupLevelTextклассаmessageи создающую локализуемый текст, который может быть отображён в интерфейсе для текущего уровня подбора.PickupLevelText<localizes>(CurrentPickupLevel : int) : message = "Уровень подбора: {CurrentPickupLevel}"Добавьте выражение
block, которое создаст виджет рабочей области и расположит текст вертикально в левой части экрана.<# Поскольку рабочая область не будет создаваться повторно во время существования панели управления счётом, добавляйте это выражение один раз при каждом создании объекта этого типа. #> block: set Canvas = canvas: Slots := array: canvas_slot: Anchors := anchors{Minimum := vector2{X := 0.0, Y := 0.25}, Maximum := vector2{X := 0.0, Y := 0.25} } Offsets := margin{Top := 0.0, Left := 25.0, Right := 0.0, Bottom := 0.0} Alignment := vector2{X := 0.0, Y := 0.0} SizeToContent := true Widget := stack_box: Orientation := orientation.Vertical Slots := array: stack_box_slot: HorizontalAlignment := horizontal_alignment.Left Widget := TotalGameScoreWidget stack_box_slot: HorizontalAlignment := horizontal_alignment.Left Widget := PendingScoreWidget stack_box_slot: HorizontalAlignment := horizontal_alignment.Left Widget := PickupLevelWidget
Класс
score_managerдолжен выглядеть следующим образом:using { /UnrealEngine.com/Temporary/UI } using { /Fortnite.com/UI } using { /Verse.org/Colors } score_manager := class: var Canvas<internal> : canvas = canvas{} TotalGameScoreWidget<internal> : text_block = text_block{DefaultTextColor := NamedColors.White} PendingScoreWidget<internal> : text_block = text_block{DefaultTextColor := NamedColors.White} PickupLevelWidget<internal> : text_block = text_block{DefaultTextColor := NamedColors.White} MaybePlayer<internal> : ?agent = false MaybePlayerUI<internal> : ?player_ui = false ScoreManagerDevice<internal> : score_manager_device = score_manager_device{} PickupLevelText<private><localizes>(InLevel : int) : message = "Уровень подбора: {InLevel}" PendingScoreText<private><localizes>(InPoints : int) : message = "Набрано очков: {InPoints}" TotalGameScoreText<private><localizes>(InPoints : int) : message = "Всего очков: {InPoints}" var TotalGameScore<private> : int = 0 var PendingScore<private> : int = 0 var PickupLevel<private> : int = 0 <# Поскольку рабочая область не будет создаваться повторно во время существования панели управления счётом, добавляйте это выражение один раз при каждом создании объекта этого типа. #> block: set Canvas = canvas: Slots := array: canvas_slot: Anchors := anchors{Minimum := vector2{X := 0.0, Y := 0.25}, Maximum := vector2{X := 0.0, Y := 0.25} } Offsets := margin{Top := 0.0, Left := 25.0, Right := 0.0, Bottom := 0.0} Alignment := vector2{X := 0.0, Y := 0.0} SizeToContent := true Widget := stack_box: Orientation := orientation.Vertical Slots := array: stack_box_slot: HorizontalAlignment := horizontal_alignment.Left Widget := TotalGameScoreWidget stack_box_slot: HorizontalAlignment := horizontal_alignment.Left Widget := PendingScoreWidget stack_box_slot: HorizontalAlignment := horizontal_alignment.Left Widget := PickupLevelWidgetСоздайте функцию
UpdateUI()со спецификаторомprivate, которая будет обновлять текст в пользовательском интерфейсе с последними значениями количества очков и текущим уровнем подбора.UpdateUI<private>() : void = if (PlayerUI := MaybePlayerUI?): PickupLevelWidget.SetText(PickupLevelText(PickupLevel)) PendingScoreWidget.SetText(PendingScoreText(PendingScore)) PendingScoreWidget.SetText(TotalGameScoreText(TotalGameScore))Создайте функцию
AddScoreManagerToUI(), которая будет обновлять пользовательский интерфейс игрока при помощи интерфейса управления счётом.AddScoreManagerToUI<public>() : void = if (PlayerUI := MaybePlayerUI?): PlayerUI.AddWidget(Canvas) UpdateUI()Создайте функцию для каждого значения, отображаемого в интерфейсе, чтобы игровой цикл мог обновлять значения:
Функцию
AddPendingScoreToTotalScore()со спецификаторомpublic. Эта функция должна добавлять набранные очки за текущую сессию к общему счёту игры и сбрасывать значение набранных очков до0. С помощьюdeferможно отложить сброс переменнойPendingScoreвместе с обновлением интерфейса и выполнить это уже после обновления переменнойTotalGameScore. Это позволит избежать использования временной переменной для хранения значенияPendingScoreдо его сброса.Verse<# Adds PendingScore to TotalGameScore and resets PendingScore to 0.#> AddPendingScoreToTotalScore<public>() : void = defer: set PendingScore = 0 UpdateUI() set TotalGameScore += PendingScoreФункцию
UpdatePendingScore()со спецификаторомpublicи целочисленным параметромPoints, который функция будет добавлять к текущему количеству очков, набранных за сессию.Verse<# Adds the given amount of points to the pending points. #> UpdatePendingScore<public>(Points : int) : void = set PendingScore += Points UpdateUI()Функцию под названием
UpdatePickupLevelсо спецификаторомpublicи целочисленным параметромLevel, который будет определять текущий уровень подбора.VerseUpdatePickupLevel<public>(Level : int) : void = set PickupLevel = Level UpdateUI()Создайте функцию
AwardScore()со спецификаторомpublic. Эта функция будет начислять очки игроку при помощи устройства «Панель управления счётом» и активировать устройство.Verse<# Awards the score to the player with the Score Manager device, by activating it. #> AwardScore<public>() : void = ScoreManagerDevice.SetScoreAward(TotalGameScore) if (AwardedPlayer := MaybePlayer?): ScoreManagerDevice.Activate(AwardedPlayer)
Класс
score_managerдолжен выглядеть следующим образом:Versescore_manager := class: <# Since we won't recreate the canvas during the score manager lifetime, do it once anytime an object of this type is created.> block: set Canvas = canvas: Slots := array: canvas_slot: Anchors := anchors{Minimum := vector2{X := 0.0, Y := 0.25}, Maximum := vector2{X := 0.0, Y := 0.25} } Offsets := margin{Top := 0.0, Left := 25.0, Right := 0.0, Bottom := 0.0} Alignment := vector2{X := 0.0, Y := 0.0} SizeToContent := trueКласс
score_managerуспешно создан, и теперь можно заняться конструктором для класса, чтобы инициализировать переменные игрока из игры. Обратите внимание: чтобы получить ссылку на интерфейс игрока, вам будет необходимо выполнить приведение типов для ссылки на игрока с типаagentк типуplayer.VerseMakeScoreManager<constructor><public>(InPlayer : agent, InScoreManagerDevice : score_manager_device) := score_manager: MaybePlayer := option{InPlayer} MaybePlayerUI := option{GetPlayerUI[player[InPlayer]]}Ваш файл score_manager.verse сейчас должен выглядеть следующим образом:
Verseusing { /UnrealEngine.com/Temporary/SpatialMath} using { /UnrealEngine.com/Temporary/UI } using { /Fortnite.com/Devices } using { /Fortnite.com/UI } using { /Verse.org/Colors } using { /Verse.org/Simulation } MakeScoreManager<constructor><public>(InPlayer : agent, InScoreManagerDevice : score_manager_device) := score_manager: MaybePlayer := option{InPlayer} MaybePlayerUI := option{GetPlayerUI[player[InPlayer]]}
Обновление счёта и интерфейса в игровом цикле
Внесите следующие изменения в файл game_coordinator_device.verse, чтобы создать и обновлять интерфейс во время игры:
Добавьте классу
game_coordinator_deviceследующие атрибуты:Переменная
ScoreManagerтипаscore_managerсо спецификаторомprivate. Этот экземпляр будет управлять интерфейсом и счётом игрока.var ScoreManager<private> : score_manager = score_manager{}Редактируемый объект
score_manager_device, который вы можете задать для устройства «Панель управления счётом» на уровне. Это устройство, которое будет использоваться классомscore_manager.@editable ScoreManagerDevice<public> : score_manager_device = score_manager_device{}Редактируемый массив целочисленных значений
PointsForPickupLevelсо спецификаторомpublicдля определения очков, которые игрок может набрать за каждый уровень подбора.@editable # Показывает, сколько очков даёт подбор в зависимости от его уровня. PointsForPickupLevel<public> : []int = array{1, 2, 3}
В функции
StartGameинициализируйте переменную панели управления счётом, вызвав конструкторMakeScoreManager()со ссылкой на игрока и устройство «Панель управления счётом», а также создайте пользовательский интерфейс для игрока.VerseStartGame<private>()<suspends> : void = Logger.Print("Trying to start the game...") # We construct a new countdown_timer that'll countdown from InitialCountdownTime once started. # Also construct a new score_manager that'll keep track of the player's score and pickup level. # The countdown_timer and score_manager require a player to show their UI to. # We should have a valid player by now: the one that entered the vehicle, triggering the game start. if (ValidPlayer := MaybePlayer?): Logger.Print("Valid player, starting game...")В функции игрового цикла
PickupDeliveryLoop()интерфейс нужно обновлять каждый раз, когда уровень подбора меняется и игрок завершает подбор или доставку:VersePickupDeliveryLoop<private>()<suspends> : void = PickupZonesTags : []pickup_zone_tag = array{pickup_zone_level_1_tag{}, pickup_zone_level_2_tag{}, pickup_zone_level_3_tag{}} MaxPickupLevel := PickupZonesTags.Length - 1 FirstPickupZoneCompletedEvent := event(){} loop: var PickupLevel : int = 0 var IsFirstPickup : logic = true # Every time the loop restarts, we should reset the pickup level UI through the ScoreManager.Когда обратный отсчёт заканчивается, игроку следует присвоить итоговый счёт. В
HandleCountdownEnd()вызовитеScoreManager.AwardScore().HandleCountdownEnd<private>(InPlayer : player)<suspends>:void= TotalTime := CountdownTimer.CountdownEndedEvent.Await() ScoreManager.AwardScore() EndGame.Activate(InPlayer)Ваш файл game_coordinator_device.verse сейчас должен выглядеть следующим образом:
Verseusing { /Verse.org/Simulation } using { /Fortnite.com/Devices } using { /Fortnite.com/Vehicles } using { /Fortnite.com/Characters } using { /Fortnite.com/Playspaces } using { /Verse.org/Random } using { /UnrealEngine.com/Temporary/Diagnostics } using { /UnrealEngine.com/Temporary/SpatialMath } using { /EpicGames.com/Temporary/Curves } using { /Verse.org/Simulation/Tags }