Nesta última etapa do tutorial Prova de Tempo: Em busca da pizza, você encontrará o código de conclusão do jogo e ideias para explorar por conta própria para esse jogo.
Código completo
Existem vários arquivos do Verse neste projeto:
- countdown_timer.verse: Consulte Cronômetro de contagem regressiva personalizado para ver o código completo do arquivo.
- game_coordinator_device.verse: Veja abaixo o código completo do arquivo.
- objective_marker.verse: Consulte o tutorial Marcador de objetivo móvel para ver o código completo do arquivo.
- pickup_delivery_zone.verse: Veja abaixo o código completo do arquivo.
- score_manager.verse: Veja abaixo o código completo do arquivo.
game_coordinate_device.verse
~~~(verse) using { /Verse.org/Simulation } using { /Fortnite.com/Devices } using { /Fortnite.com/Vehicles } using { /Fortnite.com/Characters } using { /Fortnite.com/Playspaces } using { /Verse.org/Native } using { /Verse.org/Random } using { /UnrealEngine.com/Temporary/Diagnostics } using { /UnrealEngine.com/Temporary/SpatialMath } using { /UnrealEngine.com/Temporary/Curves } using { /Verse.org/Simulation/Tags }
Tags de áreas de jogo
pickup_zone_tag
log_pizza_pursuit
game_coordinator_device
# A partir de quanto tempo o cronômetro começará a contagem regressiva.
@editable
InitialCountdownTime
@editable
EndGame
# Quantos segundos devem ser adicionados ao cronômetro de contagem regressiva quando uma retirada é entregue.
@editable
DeliveryBonusSeconds
@editable
PickupMarker
@editable
ScoreManagerDevice
@editable
PizzaRemover
@editable
# Mapeia quantos pontos uma retirada vale com base em seu nível de retirada.
PointsForPickupLevel
OnBegin
FindPlayer()
SetupZones()
# Depois de entrar no veículo, o jogador pode sair a qualquer momento; nós # queremos detectar isso todas as vezes que acontecer para colocá-lo de volta no veículo. VehicleSpawner.AgentExitsVehicleEvent.Subscribe(HandlePlayerExitsVehicle)
# Só queremos ser notificados na primeira vez que o jogador entrar no veículo para iniciar o jogo.
# StartGameOnPlayerEntersVehicle aguardará esse evento e iniciará o loop de jogo.
StartGameOnPlayerEntersVehicle()
Logger
FindPlayer
SetupZones
# Usamos etiquetas de jogo para selecionar zonas (representadas por dispositivos) com base em seu nível de dificuldade. # Usar uma matriz facilita a modificação dos níveis de dificuldade: podemos adicionar mais # níveis, aumentar/diminuir sua granularidade ou alterar sua ordem sem tocar no código. # Crie um tagged_zone_selector para cada tag de nível de dificuldade para que todos os dispositivos que tenham a mesma tag (ou seja, mesmo nível de dificuldade) # acabem no mesmo grupo de seleção. LevelTags : []pickup_zone_tag = array{pickup_zone_level_1_tag{}, pickup_zone_level_2_tag{}, pickup_zone_level_3_tag{}} set PickupZones = for (ZoneTag : LevelTags): NewZone := tagged_zone_selector{} NewZone.InitZones(ZoneTag) NewZone
StartGameOnPlayerEntersVehicle
HandlePlayerExitsVehicle
StartGame
set ScoreManager = MakeScoreManager(ValidPlayer, ScoreManagerDevice) ScoreManager.AddScoreManagerToUI()
set CountdownTimer = MakeCountdownTimer(InitialCountdownTime, ValidPlayer) CountdownTimer.StartCountdown()
# Aguardamos o encerramento da contagem regressiva. # Ao mesmo tempo, também executamos o ciclo do jogo Retirada e entrega, que constitui a interação principal. race: HandleCountdownEnd(ValidPlayer) PickupDeliveryLoop() else: Logger.Print("Não é possível encontrar um jogador válido. Anulando o início do jogo", ?Level := log_level.Error)
HandleCountdownEnd
PickupDeliveryLoop
PickupMarker.MapIndicator.Enable()
loop: var PickupLevel : int = 0 var IsFirstPickup : logic = true
<# Todas as vezes que o loop é reiniciado, devemos redefinir a IU do nível de retirada por meio do ScoreManager. O nível de retirada na IU começa em 1 (e não em 0). Alguns jogadores ficarão confusos se começar em 0. Nós indexamos a partir de 0, então PickupLevel=0 é Nível 1 na IU. #> ScoreManager.UpdatePickupLevel(PickupLevel + 1)
race: loop: if (PickupZone:base_zone = PickupZones[PickupLevel].SelectNext[]): PickupZone.ActivateZone() Sleep(0.0) PickupMarker.MoveMarker(PickupZone.GetTransform(), ?OverTime := 0.0) if (ValidPlayer := MaybePlayer?): PickupMarker.MapIndicator.ActivateObjectivePulse(ValidPlayer)
<# Esse é o único adiamento de que precisamos para qualquer PickupZone que ativarmos. A primeira PickupZone será desativada no final de cada loop externo, ou desativará qualquer PickupZone posterior. O motivo é porque a expressão é avaliada no final, quando a variável PickupZone foi vinculada a uma zona mais recente. #> defer: PickupZone.DeactivateZone()
PickupZone.ZoneCompletedEvent.Await() Logger.Print("Picked up", ?Level:=log_level.Normal)
<# Removemos as pizzas do inventário do jogador para evitar empilhá-las e fazê-las cair no chão quando a pilha estiver cheia. #> if (RemovingPlayer := MaybePlayer?): PizzaRemover.Remove(RemovingPlayer)
# Após a primeira retirada, podemos ativar a zona de entrega. if (IsFirstPickup?): set IsFirstPickup = false FirstPickupZoneCompletedEvent.Signal()
if (PickupPoints := PointsForPickupLevel[PickupLevel]): ScoreManager.UpdatePendingScore(PickupPoints)
# Atualize o nível de retirada e o ScoreManager. if (PickupLevel < MaxPickupLevel): set PickupLevel += 1 ScoreManager.UpdatePickupLevel(PickupLevel + 1) else: Logger.Print("Não é possível encontrar a próxima PickupZone para selecionar.", ?Level := log_level.Error) return # Erro proveniente de PickupDeliveryLoop block: FirstPickupZoneCompletedEvent.Await()
if (DeliveryZone := DeliveryZoneSelector.SelectNext[]): DeliveryZone.ActivateZone()
# Adiamos a desativação da zona para que o cancelamento de PickupDeliveryLoop também acabe desativando qualquer zona de entrega ativa. defer: Logger.Print("Desativando a zona de entrega.", ?Level:=log_level.Normal) DeliveryZone.DeactivateZone()
DeliveryZone.ZoneCompletedEvent.Await() Logger.Print("Entregue", ?Level:=log_level.Normal)
PointsCommitted := ScoreManager.AddPendingScoreToTotalScore() BonusTime : float = DeliveryBonusSeconds * PointsCommitted CountdownTimer.AddRemainingTime(BonusTime) else: Logger.Print("Não é possível encontrar a próxima zona de entrega para selecionar.", ?Level:=log_level.Error) return # Erro proveniente de PickupDeliveryLoop ~~~
pickup_delivery_zone.verse
~~~(verse) using { /Verse.org/Simulation } using { /Verse.org/Random } using { /Verse.org/Concurrency } using { /Verse.org/Simulation/Tags } using { /UnrealEngine.com/Temporary/SpatialMath } using { /Fortnite.com/Devices }
<# Uma zona é uma área do mapa (representada por um dispositivo) que pode ser ativada/desativada e que fornece eventos
para sinalizar quando a zona foi "Concluída" (não pode mais ser concluída até a próxima ativação).
A zona "Concluída" depende do tipo de dispositivo (ActivatorDevice) da zona.
Uso sugerido: ActivateZone() -> ZoneCompletedEvent.Await() -> DeactivateZone() #>
base_zone
GetTransform
<# Ativa a zona.
Você deve habilitar os dispositivos e quaisquer indicadores visuais da zona aqui. #>
ActivateZone
<# Desativa a Zona.
Você deve desabilitar os dispositivos e quaisquer indicadores visuais da zona aqui. #>
DeactivateZone
<# Esse evento é necessário para encerrar a corrotina WaitForZoneCompleted se a zona for desativada sem ser concluída. #>
ZoneDeactivatedEvent
WaitForZoneCompleted
MakeBaseZone
tagged_zone_selector cria zonas com base em gatilhos marcados com a tag passada para InitZones.
tagged_zone_selector
InitZones
SelectNext
### score_manager.verse
~~~(verse)
using { /UnrealEngine.com/Temporary/UI }
using { /Fortnite.com/UI }
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
MakeScoreManager<constructor><public>(InPlayer : player, InScoreManagerDevice : score_manager_device) := score_manager:
MaybePlayer := option{InPlayer}
MaybePlayerUI := option{GetPlayerUI[InPlayer]}
score_manager := class:
<# Como não recriaremos a tela durante a vida útil do gerenciador de pontuação, faça isso uma vez
sempre que um objeto desse tipo for criado. #>
block:
set Canvas = canvas:
Slots := array:
canvas_slot:
Widget := stack_box:
Orientation := orientation.Vertical
Slots := array:
stack_box_slot:
Widget := TotalGameScoreWidget
stack_box_slot:
Widget := PendingScoreWidget
stack_box_slot:
Widget := PickupLevelWidget
Offsets := margin{ Top:=0.0, Left:=500.0 }
AddScoreManagerToUI<public>() : void =
if (PlayerUI := MaybePlayerUI?):
PlayerUI.AddWidget(Canvas)
UpdateUI()
<# Adiciona PendingScore a TotalGameScore e redefine PendingScore para 0.
Retorna o número total de pontos de retirada adicionados. #>
AddPendingScoreToTotalScore<public>() : int =
set TotalGameScore += PendingScore
defer:
set PendingScore = 0
UpdateUI()
PendingScore
<# Adiciona a quantidade de pontos dada aos pontos pendentes. #>
UpdatePendingScore<public>(Points : int) : void =
set PendingScore += Points
UpdateUI()
UpdatePickupLevel<public>(Level : int) : void =
set PickupLevel = Level
UpdateUI()
<# Concede a pontuação ao jogador por meio de ScoreManagerDevice, ativando-a. #>
AwardScore<public>() : void =
ScoreManagerDevice.SetScoreAward(TotalGameScore)
if (AwardedPlayer := MaybePlayer?):
ScoreManagerDevice.Activate(AwardedPlayer)
MaybePlayer<internal> : ?player = false
MaybePlayerUI<internal> : ?player_ui = false
ScoreManagerDevice<internal> : score_manager_device = score_manager_device{}
var Canvas<internal> : canvas = canvas{}
TotalGameScoreWidget<internal> : text_block = text_block{}
PendingScoreWidget<internal> : text_block = text_block{}
PickupLevelWidget<internal> : text_block = text_block{}
PickupLevelText<private><localizes>(InLevel : int) : message = "Nível de coleta: {InLevel}"
PendingScoreText<private><localizes>(InPoints : int) : message = "Pontos pendentes: {InPoints}"
TotalGameScoreText<private><localizes>(InPoints : int) : message = "Total de pontos: {InPoints}"
var TotalGameScore<private> : int = 0
var PendingScore<private> : int = 0
var PickupLevel<private> : int = 0
UpdateUI<private>() : void =
if (PlayerUI := MaybePlayerUI?):
PickupLevelWidget.SetText(PickupLevelText(PickupLevel))
PendingScoreWidget.SetText(PendingScoreText(PendingScore))
TotalGameScoreWidget.SetText(TotalGameScoreText(TotalGameScore))
Por conta própria
Com o guia finalizado, você aprendeu a usar o Verse para criar o jogo de avaliação em tempo integral Em busca da pizza.
Usando o que você aprendeu, tente o seguinte:
- Adicionar mais níveis de zona de retirada.
- Adicionar diferentes tipos de zonas de entrega. Estenda a classe
base_zonepara que o jogador tenha que ativar algum outro dispositivo, como um botão, para completar a zona. - Fazar com que o jogador saia do veículo e complete a pé uma curta pista de obstáculos para entregar a pizza.
- Ativar várias zonas ao mesmo tempo.
- Modular os critérios de seleção da zona com base na distância do jogador.