Вы можете использовать устройство «Таймер», которое запускает таймер обратного отсчёта, однако создание собственного таймера обратного отсчёта в 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.0
RunCountdown<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 = false
var 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
Самостоятельная работа
Изучив это руководство, вы сможете создать собственный таймер обратного отсчёта.
Используя полученные знания, попробуйте сделать следующее:
Измените частоту тиков таймера и добавьте событие для каждого тика.
Добавьте к таймеру функции паузы, возобновления и перезапуска.