Вы можете использовать устройство «Таймер», которое запускает таймер обратного отсчёта, однако создание собственного таймера обратного отсчёта в Verse позволит задать ему необходимые настройки.Verse
В данном уроке описано, как создать собственный таймер с помощью Verse и вывести надпись при добавлении времени ко времени обратного отсчёта. Начнём с основ, которые вы сможете усовершенствовать в дальнейшем, переходя от проекта к проекту.
Используемые функции языка Verse
if: выражение
ifпроверяет условия и получает доступ к значениям, которые могут вызвать ошибку.block: в этом примере используется выражение
blockдля инициализации интерфейса при создании таймера обратного отсчёта.loop: выражение
loopобновляет интерфейс и завершается, когда обратный отсчёт достигает нуля.spawn: выражение
spawnзапускает асинхронное выражение в любом контексте.message: тип message означает, что текст может быть локализован, а строка, которая используется для инициализации переменной message, определяет текст и язык сообщения по умолчанию.
class: в этом примере создаётся класс Verse, который управляет обратным отсчётом и выводит его на экран.
constructor: это специальная функция, которая создаёт экземпляр связанного с ней класса.
Спецификаторы доступа: вы можете использовать спецификаторы доступа, чтобы задать уровень доступа для своего кода.
Используемые API Verse
Sleep: с помощью API
Sleep()можно выбрать период обновления интерфейса.События: вы можете создавать собственные события в Verse и добавлять пользовательские функции, выполняемые при возникновении событий.
Интерфейс на Verse: создайте собственный внутриигровой интерфейс для отображения информации об игроке и игре.
Указания
Ниже рассмотрен порядок создания собственного настраиваемого таймера. В конце этого урока для справки приведён полный сценарий.
Подготовка уровня
В этом примере используются следующие объекты окружения и устройства.
1 устройство «Кнопка»: когда игрок взаимодействует с устройством, ко времени обратного отсчёта добавляется дополнительное время.
1 устройство завершения игры: когда обратный отсчёт заканчивается, это устройство завершает игру.
Выполните следующие действия, чтобы подготовить уровень:
Добавьте на уровень устройство «Кнопка» и устройство завершения игры.
Создайте новое устройство Verse и назовите его
countdown_timer_example, используя проводник Verse. Порядок действий см. в разделе Создание собственного устройства с помощью Verse.Добавьте редактируемую ссылку на устройство «Кнопка» в
countdown_timer_exampleи назовите егоAddMoreTimeButton. Порядок действий см. в разделе Настройка свойств устройства.Versecountdown_timer_example := class(creative_device): @editable AddMoreTimeButton : button_device = button_device{}Добавьте редактируемую ссылку на устройство завершения игры в
countdown_timer_exampleи назовите егоEndGame.Versecountdown_timer_example := class(creative_device): @editable AddMoreTimeButton : button_device = button_device{} @editable EndGame : end_game_device = end_game_device{}Сохраните файл Verse и выберите в главном меню UEFN Verse > Собрать код Verse для обновления устройства на уровне, чтобы изменения отобразились на панели Сведения устройства.
Запуск обратного отсчёта
В этом примере вы создадите класс Verse, который отрисовывает собственный интерфейс и управляет своим обратным отсчётом.
Выполните следующие шаги, чтобы создать собственный таймер обратного отсчёта:
Создайте пустой файл Verse и назовите его countdown_timer.verse.
Добавьте следующие модули Verse в начале файла:
Verseusing { /UnrealEngine.com/Temporary/UI } using { /UnrealEngine.com/Temporary/SpatialMath } using { /Fortnite.com/UI } using { /Verse.org/Colors } using { /Verse.org/Simulation }Создайте класс и назовите его
countdown_timer, а затем добавьте следующие переменные:Переменная типа float с название
RemainingTimeс начальным значением0,0.Versevar RemainingTime : float = 0.0Переменная виджета типа canvas с названием
Canvas.Versevar Canvas : canvas = canvas{}Текстовый виджет с названием
RemainingTimeWidgetс белым цветом текста по умолчанию.VerseRemainingTimeWidget : text_block = text_block{DefaultTextColor := NamedColors.White}Функция, возвращающая сообщение, с названием
RemainingTimeText, которая принимает параметр типа integer для отображения значенияRemainingTime.VerseRemainingTimeText<localizes>(CurrentRemainingTime : int) : message = "{CurrentRemainingTime}"Интерфейс игрока типа option с названием
MaybePlayerUIс начальным значениемfalse.VerseMaybePlayerUI : ?player_ui = false
Ваш класс должен выглядеть следующим образом:
Versecountdown_timer := class: MaybePlayerUI : ?player_ui = false var RemainingTime : float = 0.0 var Canvas : canvas = canvas{} RemainingTimeWidget : text_block = text_block{DefaultTextColor := NamedColors.White} RemainingTimeText<localizes>(CurrentRemainingTime : int) : message = "{CurrentRemainingTime}"Добавьте выражение
block, чтобы создать интерфейс, в котором время отображается в верхней средней части экрана. Выражение block в определении класса выполняется только в момент создания экземпляра класса, поэтому в этом выраженииblockможно создать интерфейс только один раз.Versecountdown_timer := class: block: set Canvas = canvas: Slots := array: canvas_slot: Anchors := anchors: Minimum := vector2{X := 0.5, Y := 0.05} Maximum := vector2{X := 0.5, Y := 0.05} Alignment := vector2{X := 0.5, Y := 0.0} Offsets := margin{Top := 0.0, Left := 0.0, Bottom := 0.0, Right := 0.0}Добавьте функцию
StartCountdown()для отображения интерфейса.VerseStartCountdown() : void = Print("Starting countdown") if (PlayerUI := MaybePlayerUI?): PlayerUI.AddWidget(Canvas)В countdown_timer_example.verse создайте экземпляр
countdown_timerсо ссылкой на интерфейс игрока и задайте начальное время обратного отсчёта. ВызовитеStartCountdown()вOnBegin(), чтобы обратный отсчёт отображался сразу после запуска игры.Verseusing { /Verse.org/Simulation } using { /Fortnite.com/Devices } countdown_timer_example := class(creative_device): @editable AddMoreTimeButton : button_device = button_device{} @editable EndGame : end_game_device = end_game_device{}Если теперь протестировать игру, в интерфейсе не будет отображаться оставшееся время, когда начнётся обратный отсчёт, поэтому в countdown_timer.verse нужно создать функцию с названием
UpdateUI(), которая будет обновлять текущее значение обратного отсчёта в интерфейсе. ВызовитеUpdateUI()вStartCountdown().VerseStartCountdown() : void = Print("Starting countdown") if (PlayerUI := MaybePlayerUI?): PlayerUI.AddWidget(Canvas) # Update the UI when we start the timer to see the initial RemainingTime on screen UpdateUI() UpdateUI() : void =Теперь начальное значение обратного отсчёта отображается в интерфейсе, но значение не обновляется каждую секунду. Для этого:
Добавьте переменную типа float
TimerTickPeriod, чтобы указать период обновления интерфейса в секундах. В этом примере используется одна секунда.VerseTimerTickPeriod : float = 1.0 # The timer "precision": how often, in seconds, it ticks.Создайте функцию со спецификатором suspends, назовите её
RunCountdown()и вызовите её изStartCountdown(). Эта функция должна ожидать в течение времениTimerTickPeriodдо обновления интерфейса и повторять это действие в цикле. Настройте выход из цикла и уберите обратный отсчёт из интерфейса, когда таймер дойдёт до0,0.VerseStartCountdown() : void = Print("Starting countdown") if (PlayerUI := MaybePlayerUI?): PlayerUI.AddWidget(Canvas) # Update the UI when we start the timer to see the initial RemainingTime on screen UpdateUI() spawn:
При тестировании обратный отсчёт начнётся с 30 и будет обновляться каждую секунду, пока таймер не достигнет 0 и обратный отсчёт не исчезнет из интерфейса.
Добавление дополнительного времени
В этом таймере обратного отсчёта добавлен пользовательский способ добавления и вызова дополнительного времени. В этом примере показано, как добавлять дополнительное время к времени обратного отсчёта и отображать это время в момент взаимодействия игрока с устройством Кнопка.
Выполните следующие шаги, чтобы добавить дополнительное время к времени таймера обратного отсчёта в момент взаимодействия игрока с устройством «Кнопка»:
В countdown_timer.verse создайте новую функцию с названием
AddRemainingTime(), которая будет обновлять переменнуюRemainingTimeзначением, переданным функции в параметре типа float с названиемTime, а затем будет обновлять интерфейс, чтобы показать новое оставшееся время.VerseAddRemainingTime(Time : float) : void = set RemainingTime += Time # Immediately update the UI for better player feedback when time is added. UpdateUI()В countdown_timer_example.verse подпишитесь на событие
InteractedWithEventустройства «Кнопка» и вызовитеAddRemainingTime()в момент взаимодействия игрока с этим устройством.Verseusing { /Verse.org/Simulation } using { /Fortnite.com/Devices } countdown_timer_example := class(creative_device): @editable AddMoreTimeButton : button_device = button_device{} @editable EndGame : end_game_device = end_game_device{}Добавьте виджет к классу
countdown_timerдля вывода информации о том, сколько времени добавляется ко времени обратного отсчёта при нажатии кнопки игроком.VerseAddedTimeWidget : text_block = text_block{DefaultTextColor := NamedColors.White} AddedTimeText<localizes>(AddedTime : int) : message = " +{AddedTime}!"Используйте для нового виджета AddedTimeWidget те же параметры расположения, что и для виджета RemainingTime, но измените следующие значения, чтобы надпись со временем отображалась в правом верхнем углу таймера обратного отсчёта:
Для AddedTimeWidget установите отступ слева в Offsets равным
50,0.Для RemainingTimeWidget установите отступ сверху в Offsets равным
25,0.Versecountdown_timer := class: <# This block runs for each instance of the countdown_timer class. We can setup the canvas once here. #> block: set Canvas = canvas: Slots := array: canvas_slot: Anchors := anchors: Minimum := vector2{X := 0.5, Y := 0.05} Maximum := vector2{X := 0.5, Y := 0.05}
Создайте новую функцию с названием
AddedTimeCallout(), которая обновляет значение в AddedTimeWidget и отображает надпись в течение двух секунд, прежде чем снова скрыть виджет. ВызовитеAddedTimeCallout()вAddRemainingTime().VerseAddRemainingTime(Time : float) : void = set RemainingTime += Time # Immediately update the UI for better player feedback when time is added. UpdateUI() # Fire a simple callout to show the time being added. spawn: AddedTimeCallout(Time)При тестировании обратный отсчёт начнётся с 30 и будет обновляться каждую секунду, пока таймер не достигнет 0 и обратный отсчёт не исчезнет из интерфейса. Когда игрок взаимодействует с кнопкой, к обратному отсчёту добавляется двадцать секунд, и на две секунды появляется надпись с добавленным временем.
Оповещение о завершения обратного отсчёта таймера
Ранее в этом уроке мы использовали событие InteractedWithEvent устройства «Кнопка», чтобы узнать, когда игрок нажал кнопку, и добавить время к таймеру обратного отсчёта. Но также можно создавать собственные пользовательские события для использования другим кодом, чтобы этот код «знал», когда что-то происходит в вашем коде.
В этом примере показано, как использовать следующее поведение пользовательских событий:
Signal(): эта функция позволяет всем, кто ожидает событие, узнать, что событие произошло.Await(): эта асинхронная функция блокирует выполнение окружающего её контекста до тех пор, пока не поступит сигнал о событии.
В этом примере к таймеру обратного отсчёта добавлено событие, сигнализирующее об окончании обратного отсчёта, чтобы вы могли активировать устройство завершения игры.
Выполните следующие шаги, чтобы добавить событие окончания обратного отсчёта.
Добавьте поле события с названием
CountdownEndedEventв классcountdown_timer:VerseCountdownEndedEvent : event() = event(){}event()имеет параметрический тип, то есть это событие возвращает класс или интерфейс, а не значение или экземпляр объекта. Вот почему используется типevent(), и поэтому нужно инициализировать константуCountdownEndedEventс помощьюevent(){}, чтобы имитировать создание экземпляра класса.Обновите
RunCountdown()чтобы подать сигнал оCountdownEndedEventи чтобы другой код «узнал» об окончании обратного отсчёта, прежде чем выйти из цикла.VerseRunCountdown()<suspends> : void = # We loop with the TimerTickPeriod. # The UI is also updated each time. loop: Sleep(TimerTickPeriod) set RemainingTime -= TimerTickPeriod UpdateUI() # Timer End if (RemainingTime <= 0.0):В countdown_timer_example.verse дождитесь события
CountdownEndedEvent, связанного сCountdownTimer, а затем активируйте устройство завершения игры, поскольку событие указывает на завершение обратного отсчёта.VerseOnBegin<override()<suspends> : void = AddMoreTimeButton.InteractedWithEvent.Subscribe(OnButtonInteractedWith) if: FirstPlayer := Self.GetPlayspace().GetPlayers()[0] PlayerUI := GetPlayerUI[player[FirstPlayer]] then: set CountdownTimer = countdown_timer{MaybePlayerUI := option{PlayerUI}, RemainingTime := InitialCountdownTime} CountdownTimer.StartCountdown() CountdownTimer.CountdownEndedEvent.Await()При тестировании обратный отсчёт начнётся с 30 и будет обновляться каждую секунду, пока таймер не достигнет 0. Как только обратный отсчёт закончится, он исчезнет из интерфейса и игра завершится. Когда игрок взаимодействует с кнопкой, к обратному отсчёту добавляется двадцать секунд, и на две секунды появляется надпись, показывающая добавленное время.
Подготовка класса к использованию другим кодом
Теперь мы создали собственный класс таймера обратного отсчёта и использовали Verse-устройство для создания экземпляра таймера и управления им.
При создании собственных пользовательских классов (да и вообще любого кода) важно указать, кто может получить доступ к тому, что вы создаёте. Например, только таймер обратного отсчёта должен создавать и изменять свой интерфейс. В Verse можно использовать спецификатор доступа, чтобы задать уровень доступа для своего кода.
Добавьте спецификатор public ко всем идентификаторам, к которым могут иметь доступ другие пользователи, потому что public означает, что идентификатор является общедоступным. В этом примере в устройстве countdown_timer_example используются все следующие элементы, поэтому к ним должен предоставляться общий доступ:
CountdownEndedEvent<public> : event() = event(){}StartCountdown<public>() : void =AddRemainingTime<public>(Time : float) : void =
Добавьте спецификатор private ко всем идентификаторам, к которым не должны иметь доступ другие пользователи, потому что private означает, что доступ к идентификатору возможен только в текущей, непосредственно окружающей области видимости (в данном случае это класс countdown_timer).
В нашем примере частный доступ должен предоставляться к следующим элементам:
RemainingTimeWidget<private> : text_block = text_block{DefaultTextColor := NamedColors.White}AddedTimeWidget<private> : text_block = text_block{DefaultTextColor := NamedColors.White}AddedTimeText<localizes><private>(AddedTime : int) : message = " +{AddedTime}!"RemainingTimeText<localizes><private>(CurrentRemainingTime : int) : message = "{CurrentRemainingTime}"var Canvas<private> : canvas = canvas{}TimerTickPeriod<private> : float = 1.0RunCountdown<private>()<suspends> : void =AddedTimeCallout<private>(Time : float)<suspends> : void =UpdateUI<private>() : void =
Рекомендуется группировать код по уровню доступа. Советуем упорядочить код от наибольшего уровня доступа к наименьшему:
публичный
Внутреннее
защищённый
частный
Можно использовать конструктор для задания начальных значений нового экземпляра класса без вывода в редактор переменных класса. Конструктор — это специальная функция, которая создаёт экземпляр связанного с ней класса.
Создайте конструктор для класса countdown_timer, который обновляет переменные RemainingTime и MaybePlayerUI.
MakeCountdownTimer<constructor><public>(MaxTime : float, InPlayer : agent) := countdown_timer:
RemainingTime := MaxTime
MaybePlayerUI := option{GetPlayerUI[player[InPlayer]]}
К переменным RemainingTime и MaybePlayerUI, заданным в конструкторе, не должен предоставляться общий доступ, но они не могут иметь частного доступа, если они настраиваются в конструкторе. Вы можете использовать спецификатор internal для этих переменных, при этом доступ к идентификатору будет возможен только в текущем, непосредственно окружающем модуле.
MaybePlayerUI<internal> : ?player_ui = falsevar RemainingTime<internal> : float = 0.0
Полный код
Ниже приведён полный код для создания пользовательского таймера обратного отсчёта.
В этом примере создано два файла Verse.
countdown_timer.verse
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /UnrealEngine.com/Temporary/UI }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /Verse.org/Simulation }
using { /Fortnite.com/UI }
MakeCountdownTimer<constructor><public>(MaxTime : float, InPlayer : agent) := countdown_timer:
RemainingTime := MaxTime
MaybePlayerUI := option{GetPlayerUI[player[InPlayer]]}
countdown_timer_example.verse
using { /Verse.org/Simulation }
using { /Fortnite.com/Devices }
using { /UnrealEngine.com/Temporary/UI }
countdown_timer_example := class(creative_device):
@editable
AddMoreTimeButton : button_device = button_device{}
@editable
Самостоятельная работа
Изучив это руководство, вы сможете создать собственный таймер обратного отсчёта.
Используя полученные знания, попробуйте сделать следующее:
Измените частоту тиков таймера и добавьте событие для каждого тика.
Добавьте к таймеру функции паузы, возобновления и перезапуска.