В этом уроке для реализации некоторых функций используется язык Verse. Воспользовавшись кодом Verse из этого урока, вы сможете:
-
Периодически запускать и воспроизводить анимации для создания игровой механики.
-
Обновлять информацию о счёте для всех игроков в мини-игре.
-
Настраивать задержки появления игроков и время периодического появления предметов.
Вы можете использовать этот код повторно, чтобы:
-
Включать и отключать устройства, когда игра переходит из режима ЦЕНТРА в режим игрового процесса и обратно в режим ЦЕНТРА.
-
Проверять счёт игроков, чтобы определить лучший результат и отобразить его в ЦЕНТРЕ.
-
Завершить игру и телепортировать игроков обратно в ЦЕНТР по окончании мини-игры.
Настройка устройства Verse и редактируемых объектов
Настройте устройство Verse и редактируемые объекты:
-
Создайте новое устройство Verse с именем
tiltnboom
и добавьте его на уровень. Порядок действий см. в разделе Создание собственного устройства с помощью Verse. -
Добавьте следующие модули в начале файла:
using { /Fortnite.com/Characters } using { /Fortnite.com/Devices } using { /Fortnite.com/Game } using { /UnrealEngine.com/Temporary/Diagnostics } using { /Verse.org/Random } using { /Verse.org/Simulation }
- Добавьте следующие поля в определение класса
tiltnboom
:-
Два редактируемых устройства
trigger
: ActivateGameTrigger и EndGameTrigger.ActivateGameTrigger
используется для активации этой игры. Триггер может быть активирован чем угодно, например телепортом в игру или завершением вступительного видеоролика.EndGameTrigger
используется для завершения мини-игры и последующего удаления всех камер и устройств управления у игроков.
@editable ActivateGameTrigger:trigger_device = trigger_device{} @editable EndGameTrigger:trigger_device = trigger_device{}
-
Редактируемое устройство «Таймер» с именем GameTimer. Через определённое время завершает игру.
-
Массив (
array
) устройств «Последовательность внутриигрового видео» с именемCannonballSequences
. Каждое устройство в этом массиве воспроизводит попадание пушечного ядра в определённое место. -
Массив зон поражения с именем DamageVolumes, которые включаются и отключаются в зависимости от состояния игры.
-
Массив генераторов предметов с именем ItemSpawners, которые случайным образом создают случайное количество предметов во время игры.
-
Устройство «Область захвата» с именем CaptureArea. Начисляет игрокам очки за нахождение на плоту и включается/отключается в зависимости от состояния игры.
-
Устройство «Панель управления счётом» с именем
ScoreManager
, которое сбрасывает счёт игроков в начале игры и определяет победителя в конце игры. -
Телепорт с именем
HUBTeleporter
, который возвращает всех игроков в ЦЕНТР по окончании игры. -
Устройство «Упоминание игрока» с именем PlayerReference, которое отображает победителя в ЦЕНТРЕ между играми.
@editable GameTimer:timer_device = timer_device{} @editable CannonballSequences:[]cinematic_sequence_device = array{} @editable DamageVolumes:[]damage_volume_device = array{} @editable ItemSpawners:[]item_spawner_device = array{} @editable CaptureArea:capture_area_device = capture_area_device{} @editable ScoreManager:score_manager_device = score_manager_device{} @editable HUBTeleporter:teleporter_device = teleporter_device{} @editable PlayerReference:player_reference_device = player_reference_device{}
-
-
Два редактируемых массива точек появления игроков с именами
PlayerSpawners
иHUBSpawners
. Отключают появление и возвращают игроков в ЦЕНТР после завершения мини-игры.@editable PlayerSpawners:[]player_spawner_device = array{} @editable HUBSpawners:[]player_spawner_device = array{}
-
Две редактируемые переменные типа
float
с именамиMinimumItemSpawnTime
иMaximumItemSpawnTime
. Это минимальное и максимальное время ожидания между появлением предметов на плоту.@editable MinimumItemSpawnTime:float = 5.0 @editable MaximumItemSpawnTime:float = 10.0
-
Редактируемая переменная типа
float
с именемDelayAfterGame
. Это задержка телепортации игроков обратно в ЦЕНТР после завершения игры.@editable DelayAfterGame:float = 5.0
-
Переменная
float
с именемDelayBetweenCannonballSequences
. Задержка появления пушечных ядер между последовательностями на протяжении всей мини-игры.DelayBetweenCannonballSequences:float = 8.0
-
Логическая переменная с именем
GameActive
, которая определяет активное или неактивное состояние игры.var GameActive:logic = false
Начало игры
Когда начинается мини-игра, включается несколько устройств и счёт игрока сбрасывается на 0.
-
Перед определением класса
tiltnboom
добавьте канал записи в журнал сообщений, относящихся к мини-игре. Затем добавьте средство ведения журнала в определение класса для использования с каналом ведения журнала.tiltnboom_log_channel := class(log_channel){} # Это Verse-устройство творческого режима, которое можно разместить на уровне tiltnboom := class(creative_device): Logger:log = log{Channel := tiltnboom_log_channel}
-
Добавьте в определение класса
titnboom
новый методOnTriggered
, который принимает значениеInitiatingAgent
и запускает игру. Добавьте выражениеif
в метод, чтобы он возвращал управление, если игра уже активна, поскольку нам не нужно запускать игру, если она уже запущена.OnTriggered(InitiatingAgent:?agent):void= if (GameActive?): return spawn{StartGame()}
-
В
OnBegin()
подпишите событиеTriggeredEvent
в ActivateGameTrigger на функциюOnTriggered
, чтобы запускать игру. Любое устройство, которое необходимо включить в начале мини-игры, будет подписано на это событие и включится при возникновении события OnTrigger.OnBegin<override>()<suspends>:void= ActivateGameTrigger.TriggeredEvent.Subscribe(OnTriggered)
-
Добавьте новый метод
StartGame()
, управляющий логикой запуска игры. Добавьте модификатор<suspends>
к этой функции, чтобы она могла запускаться асинхронно.Сначала задайте для
GameActive
значениеtrue
, чтобы сигнализировать о том, что игра активна, а затем включитеCaptureArea
и каждую зонуDamageVolume
в массивеDamageVolumes
.Сбросьте на ноль счёт каждого игрока, у которого он больше 0. Для этого начислите такому игроку количество очков, противоположное текущему; в результате счёт станет равен 0.
Затем в выражении race задайте конкурентное выполнение завершения отсчёта таймера, последовательностей запуска пушечных ядер и появления случайных предметов. Последовательность запуска пушечных ядер и появление случайных предметов выполняются в бесконечных циклах, но они прерываются, как только таймер завершает отсчёт.
По завершении игры вызовите функцию
OnGameFinished()
, чтобы выполнить очистку.StartGame()<suspends>:void= Logger.Print("Игра начинается.") set GameActive = true CaptureArea.Enable() for (DamageVolume : DamageVolumes): DamageVolume.Enable() for: Player : GetPlayspace().GetPlayers() PlayerScore := ScoreManager.GetCurrentScore(Player) PlayerScore > 0 do: ScoreManager.SetScoreAward(-PlayerScore) ScoreManager.Activate(Player) race: GameTimer.SuccessEvent.Await() StartCannonSequence() SpawnRandomItems() OnGameFinished()
Создание цикла пушечного ядра
В этом разделе мы создадим функцию, которая воспроизводит последовательности внутриигрового видео для пушечных ядер.
-
Добавьте в определение класса
tiltnboom
новый методStartCannonSequence()
, который воспроизводит видеоряды для пушечных ядер и использует цикл для выполнения выражения задержки между анимациями пушечных ядер. Добавьте модификатор<suspends>
к этой функции, чтобы она могла запускаться асинхронно.В цикле выберите случайную последовательность из массива
CannonballSequences
, определив индекс элемента с помощьюGetRandomInt()
. Воспроизведите последовательность, а затем вставьте задержку (Sleep) длительностьюDelayBetweenCannonballSequences
, прежде чем воспроизводить другую последовательность.StartCannonSequence()<suspends>:void= loop: RandomCannonballSequence:int = GetRandomInt(0, CannonballSequences.Length - 1) if (CannonballSequence := CannonballSequences[RandomCannonballSequence]): Logger.Print("В качестве CannonballSequence выбрана {RandomCannonballSequence}") CannonballSequence.Play() Sleep(DelayBetweenCannonballSequences)
Создание циклов случайного появления предметов
Этот раздел посвящён созданию функции, которая обеспечивает повторное появление случайных предметов в случайных местах на плоту, что может повышать или понижать шансы игроков на победу в мини–игре.
-
В определении класса
titlnboom
добавьте новый методSpawnRandomItems
. Этот метод управляет появлением предметов на плоту. -
Получите количество предметов в массиве
ItemSpawners
, а затем переберите элементы массива в цикле. Используйте случайное числоint
, чтобы получить случайный генератор предметов из массива, а затем активируйте его. Вставьте случайную задержку (Sleep) между появлением предметов. -
В цикле случайным образом определяется, сколько предметов нужно создать, и случайным образом выбирается предмет для создания
NumberOfItemsToSpawn
раз.DelayBetweenItemSpawns
задаёт неопределённое время ожидания между появлением предметов.SpawnRandomItems()<suspends>:void= ItemSpawnerCount:int = ItemSpawners.Length - 1 loop: NumberOfItemsToSpawn:int = GetRandomInt(0, ItemSpawnerCount) # Создадим случайно выбранный предмет NumberOfItemsToSpawn раз. for: CurrentItemSpawnNumber := 0..NumberOfItemsToSpawn RandomItemToSpawn:int = GetRandomInt(0, ItemSpawnerCount) SelectedItemSpawner := ItemSpawners[RandomItemToSpawn] do: SelectedItemSpawner.SpawnItem() Logger.Print("Случайные предметы созданы.") DelayBetweenItemSpawns:float = GetRandomFloat(MinimumItemSpawnTime, MaximumItemSpawnTime)
Завершение игры
В конце мини-игры нужно отправить результат победителя в ЦЕНТР, отключить устройства и телепортировать игроков обратно в ЦЕНТР.
-
В определении класса
titlnboom
добавьте новый методOnGameFinished
. Когда игра закончится, эта функция переведёт её в неактивный режим и отключит соответствующие устройства.OnGameFinished()<suspends>:void= Logger.Print("Конец игры.") set GameActive = false CaptureArea.Disable() for (PlayerSpawner : PlayerSpawners): PlayerSpawner.Disable() for (DamageVolume : DamageVolumes): DamageVolume.Disable()
-
Добавьте переменную
HighestScore
типаint
для отслеживания игрока с лучшим счётом и переменную-ссылкуWinningPlayer
типа option на этого игрока.var HighestScore:int = -1 var WinningPlayer:?agent = false
-
В выражении
for
/do получите всех игроков в игровом пространстве, а затем получитеFortCharacter
для каждого из них. Запретите игроку перемещаться с помощьюPutInStasis()
, передав новый наборstasis_args
, позволяющий проявлять эмоции и поворачиваться, чтобы игроки могли повеселиться в игре.for: Player : GetPlayspace().GetPlayers() FortCharacter := Player.GetFortCharacter[] do: FortCharacter.PutInStasis(stasis_args{AllowTurning := true, AllowEmotes := true})
-
С помощью выражения
if
проверьте счёт каждого игрока, чтобы найти и сохранить лучший счёт в устройстве «Упоминание игрока» в ЦЕНТРЕ.if (ScoreManager.GetCurrentScore(Player) > HighestScore): set HighestScore = ScoreManager.GetCurrentScore(Player) set WinningPlayer = option{Player}
-
Наконец, в другом выражении
if
вызовитеTeleportPlayersToHUB()
, чтобы телепортировать всех игроков обратно в ЦЕНТР, когда лучший счёт будет определён.if(Winner := WinningPlayer?): PlayerReference.Register(Winner) TeleportPlayersToHUB()
Телепортация игроков обратно в ЦЕНТР
Когда будет определён счёт всех игроков и объявлен победитель, все игроки должны телепортироваться обратно в ЦЕНТР.
-
В определении класса
titlnboom
добавьте новый методTeleportPlayersToHUB()
. Этот метод включает все генераторы игроков в ЦЕНТРЕ, а затем ждёт несколько секунд, прежде чем телепортировать всех обратно в ЦЕНТР. Также этот метод должен активироватьEndGameTrigger
, чтобы удалить камеру и устройства управления у игроков.TeleportPlayersToHUB()<suspends>:void= for (HUBSpawner : HUBSpawners): HUBSpawner.Enable() Sleep(DelayAfterGame) EndGameTrigger.Trigger()
-
В выражении
for
телепортируйте каждого игрока обратно вHUBTeleporter
и освободите его из стазиса.for: Player : GetPlayspace().GetPlayers() FortCharacter := Player.GetFortCharacter[] do: HUBTeleporter.Teleport(Player) Sleep(1.0) FortCharacter.ReleaseFromStasis()
Самостоятельная работа
Измените этот код, чтобы использовать устройство «Таймер» для решения других задач. Вместо того чтобы определять продолжительность мини-игры, можно использовать устройство «Таймер» для задания времени конкурентного выполнения.
using { /Fortnite.com/Characters }
using { /Fortnite.com/Devices }
using { /Fortnite.com/Game }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /Verse.org/Random }
using { /Verse.org/Simulation }
tiltnboom_log_channel := class(log_channel){}
# Это Verse-устройство творческого режима, которое можно разместить на уровне
tiltnboom := class(creative_device):
Logger:log = log{Channel := tiltnboom_log_channel}
# Триггер, используемый для активации этой игры. Триггер может быть активирован чем угодно, например телепортом в игру или завершением вступительного видеоролика.
@editable
ActivateGameTrigger:trigger_device = trigger_device{}
# Триггер, используемый для завершения этой игры. Применяется для удаления у игроков камеры и устройств управления по завершении игры.
@editable
EndGameTrigger:trigger_device = trigger_device{}
# Устройство «Таймер» используется для завершения игры через определённое время.
@editable
GameTimer:timer_device = timer_device{}
# Массив последовательностей, в котором каждая последовательность воспроизводит попадание пушечного ядра в определённое место.
@editable
CannonballSequences:[]cinematic_sequence_device = array{}
# Массив зон поражения для их включения/отключения в зависимости от состояния игры.
@editable
DamageVolumes:[]damage_volume_device = array{}
# Массив генераторов игроков для данной игры, используемый для отключения этих генераторов по завершении игры.
@editable
PlayerSpawners:[]player_spawner_device = array{}
# Массив генераторов игроков в ЦЕНТРЕ, используемый для отключения этих генераторов по завершении игры, чтобы игроки смогли вернуться в ЦЕНТР.
@editable
HUBSpawners:[]player_spawner_device = array{}
# Массив генераторов предметов для случайного создания случайного количества предметов во время игры.
@editable
ItemSpawners:[]item_spawner_device = array{}
# Устройство «Область захвата», которое начисляет игрокам очки за нахождение на плоту. Используется для его включения/отключения в зависимости от состояния игры.
@editable
CaptureArea:capture_area_device = capture_area_device{}
# Устройство «Панель управления счётом» используется для сброса счёта игроков в начале игры и определения победителя в конце игры.
@editable
ScoreManager:score_manager_device = score_manager_device{}
# Телепорт, используемый для переноса всех игроков обратно в ЦЕНТР по завершении игры.
@editable
HUBTeleporter:teleporter_device = teleporter_device{}
# Минимальное время между появлением случайных предметов.
@editable
MinimumItemSpawnTime:float = 5.0
# Максимальное время между появлением случайных предметов.
@editable
MaximumItemSpawnTime:float = 10.0
# Устройство «Упоминание игрока», которое отображает победителя в ЦЕНТРЕ между играми.
@editable
PlayerReference:player_reference_device = player_reference_device{}
# Время ожидания после завершения игры до переноса всех игроков обратно в ЦЕНТР.
@editable
DelayAfterGame:float = 5.0
# Время ожидания между воспроизведением последовательностей запуска пушечных ядер.
DelayBetweenCannonballSequences:float = 8.0
# Глобальная логическая переменная активного/неактивного состояния игры.
var GameActive:logic = false
OnBegin<override>()<suspends>:void=
# Подпишемся на событие TriggeredEvent триггера ActivateGameTrigger, чтобы при срабатывании триггера устройство могло запустить игру.
ActivateGameTrigger.TriggeredEvent.Subscribe(OnTriggered)
# Запускает игру при срабатывании ActivateGameTrigger,
# если игра ещё не активна.
OnTriggered(InitiatingAgent:?agent):void=
# Не запускаем игру, если она уже запущена.
if (GameActive?):
return
spawn{StartGame()}
# Если игра не была активна и была запущена, изменим её режим на активный и включим устройства для игры.
# Затем обнулим счёт всех игроков, подпишемся на событие SuccessEvent устройства «Таймер», чтобы завершить игру, когда истечёт время,
# и начнём запускать в игроков пушечные ядра и создавать случайные предметы.
StartGame()<suspends>:void=
Logger.Print("Игра начинается.")
set GameActive = true
# Включим устройства, используемые в игре.
CaptureArea.Enable()
for (DamageVolume : DamageVolumes):
DamageVolume.Enable()
# Сбросим на ноль счёт каждого игрока, у которого он больше **0**. Для этого начислим такому игроку количество очков, противоположное текущему;
# в результате счёт станет равен **0**.
for:
Player : GetPlayspace().GetPlayers()
PlayerScore := ScoreManager.GetCurrentScore(Player)
PlayerScore > 0
do:
ScoreManager.SetScoreAward(-PlayerScore)
ScoreManager.Activate(Player)
# Зададим конкурентное выполнение завершения отсчёта таймера, последовательностей запуска пушечных ядер и появления случайных предметов.
# Последовательность запуска пушечных ядер и появление случайных предметов выполняются в бесконечных циклах, но они прерываются, как только таймер завершает отсчёт.
race:
GameTimer.SuccessEvent.Await()
StartCannonSequence()
SpawnRandomItems()
# Игра завершилась, потому что таймер завершил отсчёт. Переместим игроков и определим игрока с лучшим счётом.
OnGameFinished()
# Если во время выполнения цикла игра становится неактивной, выйдем из цикла и прекратим запускать пушечные ядра.
# В противном случае выберем случайную последовательность из массива и воспроизведём её, а затем подождём восемь секунд, прежде чем воспроизводить другую последовательность.
StartCannonSequence()<suspends>:void=
loop:
# Выберем для воспроизведения случайную последовательность запуска пушечных ядер.
RandomCannonballSequence:int = GetRandomInt(0, CannonballSequences.Length - 1)
if (CannonballSequence := CannonballSequences[RandomCannonballSequence]):
Logger.Print("В качестве CannonballSequence выбрана {RandomCannonballSequence}")
# Воспроизведём случайную последовательность запуска пушечного ядра
CannonballSequence.Play()
# Подождём DelayBetweenCannonBallSequences секунд, прежде чем воспроизводить следующую последовательность.
Sleep(DelayBetweenCannonballSequences)
# Получим длину массива ItemSpawners, затем переберём элементы массива в цикле и активируем случайное число генераторов предметов.
# Подождём случайное время между появлением предметов. Будем повторять цикл, пока игра активна.
SpawnRandomItems()<suspends>:void=
ItemSpawnerCount:int = ItemSpawners.Length - 1
loop:
# Случайным образом определим, сколько предметов должно появиться на этот раз.
NumberOfItemsToSpawn:int = GetRandomInt(0, ItemSpawnerCount)
# Создадим случайно выбранный предмет NumberOfItemsToSpawn раз.
for:
CurrentItemSpawnNumber := 0..NumberOfItemsToSpawn
RandomItemToSpawn:int = GetRandomInt(0, ItemSpawnerCount)
SelectedItemSpawner := ItemSpawners[RandomItemToSpawn]
do:
SelectedItemSpawner.SpawnItem()
# Сгенерируем случайное время ожидания между появлением элементов.
Logger.Print("Случайные предметы созданы.")
DelayBetweenItemSpawns:float = GetRandomFloat(MinimumItemSpawnTime, MaximumItemSpawnTime)
# Подождём DelayBetweenItemSpawns секунд
Sleep(DelayBetweenItemSpawns)
# Когда игра закончится, переведём её в неактивный режим и отключим соответствующие устройства.
# Затем определим игрока-победителя и укажем его в устройстве «Упоминание игрока».
# Наконец, телепортируем всех обратно в ЦЕНТР.
OnGameFinished()<suspends>:void=
Logger.Print("Конец игры.")
set GameActive = false
# Отключим устройства, используемые в игре.
CaptureArea.Disable()
for (PlayerSpawner : PlayerSpawners):
PlayerSpawner.Disable()
for (DamageVolume : DamageVolumes):
DamageVolume.Disable()
# Затем найдём игроков с лучшим счётом и сохраним их устройстве «Упоминание игрока».
var HighestScore:int = -1
var WinningPlayer:?agent = false
for:
Player : GetPlayspace().GetPlayers()
FortCharacter := Player.GetFortCharacter[]
do:
# Запретим игроку перемещаться.
FortCharacter.PutInStasis(stasis_args{AllowTurning := true, AllowEmotes := true})
# Прверим, не превышает ли счёт этого игрока лучший найденный счёт.
if (ScoreManager.GetCurrentScore(Player) > HighestScore):
# Обновим лучший счёт и игрока-победителя
set HighestScore = ScoreManager.GetCurrentScore(Player)
set WinningPlayer = option{Player}
if(Winner := WinningPlayer?):
PlayerReference.Register(Winner)
TeleportPlayersToHUB()
# Включим все генераторы игроков в ЦЕНТРЕ, а затем подождём несколько секунд, прежде чем телепортировать всех обратно в ЦЕНТР.
# Также активируем EndGameTrigger, чтобы удалить камеру и устройства управления у игроков.
TeleportPlayersToHUB()<suspends>:void=
# Включим генераторы ЦЕНТРА.
for (HUBSpawner : HUBSpawners):
HUBSpawner.Enable()
# Задержка после завершения игры.
Sleep(DelayAfterGame)
# Сообщим другим устройствам о том, что игра завершена.
EndGameTrigger.Trigger()
# Переместим всех игроков в ЦЕНТР.
for:
Player : GetPlayspace().GetPlayers()
FortCharacter := Player.GetFortCharacter[]
do:
HUBTeleporter.Teleport(Player)
# Подождём одну секунду до завершения телепортации.
Sleep(1.0)
# Разрешим игроку перемещаться.
FortCharacter.ReleaseFromStasis()