Time Trial: Pizza Pursuit eğitiminin son adımında oyunun tam kodunu ve bu oyun için kendi başına keşfedeceğin fikirleri bulacaksın.
Tam Kod
Bu projede birden çok Verse dosyası bulunur:
- countdown_timer.verse: Dosyanın tam kodu için Özel Geri Sayım Süreölçeri bölümüne bakabilirsin.
- game_coordinator_device.verse: Dosyanın tam kodunu aşağıda görebilirsin.
- objective_marker.verse: Dosyanın tam kodunu Hareketli Hedef İşareti eğitiminde bulabilirsin.
- pickup_delivery_zone.verse: Dosyanın tam kodunu aşağıda görebilirsin.
- score_manager.verse: Dosyanın tam kodunu aşağıda görebilirsin.
game_coordinate_device.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 }
# Oyun bölgeleri etiketleri
pickup_zone_tag<public> := class(tag):
pickup_zone_level_1_tag<public> := class(pickup_zone_tag):
pickup_zone_level_2_tag<public> := class(pickup_zone_tag):
pickup_zone_level_3_tag<public> := class(pickup_zone_tag):
delivery_zone_tag<public> := class(tag):
log_pizza_pursuit<internal> := class(log_channel){}
game_coordinator_device<public> := class(creative_device):
@editable
VehicleSpawner<public> : vehicle_spawner_atk_device = vehicle_spawner_atk_device{}
# Geri sayım süreölçerinin ne kadar süre geri saymaya başlayacağı.
@editable
InitialCountdownTime<public> : float = 30.0
@editable
EndGame<public> : end_game_device = end_game_device{}
# Bir teslimat yapıldığında geri sayım süreölçerine kaç saniye ekleneceği.
@editable
DeliveryBonusSeconds<public> : float = 20.0
@editable
PickupMarker<public> : objective_marker = objective_marker{}
@editable
ScoreManagerDevice<public> : score_manager_device = score_manager_device{}
@editable
PizzaRemover<public> : item_remover_device = item_remover_device{}
@editable
# Bir alma işleminin alma seviyesine göre kaç puan değerinde olduğunu belirler.
PointsForPickupLevel<public> : []int = array{1, 2, 3}
OnBegin<override>()<suspends> : void =
FindPlayer()
SetupZones()
# Araca girdikten sonra oyuncu dilediği zaman çıkabilir.
# Oyuncuyu araca geri koymak için bu olay her gerçekleştiğinde saptamak isteriz.
VehicleSpawner.AgentExitsVehicleEvent.Subscribe(HandlePlayerExitsVehicle)
# Oyunu başlatmak için yalnızca oyuncu araca ilk kez girdiğinde bildirim almak istiyoruz.
# StartGameOnPlayerEntersVehicle olayı bekler ve sonra oynanış döngüsünü başlatır.
StartGameOnPlayerEntersVehicle()
Logger<private> : log = log{Channel := log_pizza_pursuit}
var MaybePlayer<private> : ?player = false
var CountdownTimer<private> : countdown_timer = countdown_timer{}
var ScoreManager<private> : score_manager = score_manager{}
DeliveryZoneSelector<private> : tagged_zone_selector = tagged_zone_selector{}
var PickupZones<private> : []tagged_zone_selector = array{}
FindPlayer<private>() : void =
# Bu bir tek oyunculu deneyim olduğundan ilk oyuncu (0)
# tek kullanılabilir oyuncu olmalıdır.
Playspace := Self.GetPlayspace()
if (FirstPlayer := Playspace.GetPlayers()[0]):
set MaybePlayer = option{FirstPlayer}
Logger.Print("Player found")
else:
# Bir oyuncu bulamazsak günlüğe bir hata kaydet.
# Her zaman en az bir oyuncu mevcut olduğundan böyle bir durum yaşanmamalıdır.
Logger.Print("Geçerli oyuncu bulunamıyor", ?Level := log_level.Error)
SetupZones<private>() : void =
# Zorluk seviyesine göre ölçeklendirilmediği için yalnızca bir tür teslim bölgesi vardır.
DeliveryZoneSelector.InitZones(delivery_zone_tag{})
# Bölgeleri (cihazlarla temsil edilir) zorluk seviyesine göre seçmek için oynanış etiketlerini kullanırız.
# Dizi kullanmak, zorluk seviyelerini değiştirmeyi kolaylaştırır: Koda dokunmadan
# daha fazla bölüm ekleyebilir, ayrıntı seviyesini artırabilir/azaltabilir veya sırasını değiştirebiliriz.
# Aynı etikete (aynı zorluk seviyesine) sahip tüm cihazların aynı seçim havuzunda olması için her bir zorluk seviyesi etiketi için
# bir one tagged_zone_selector oluştur.
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<private>()<suspends> : void =
VehiclePlayer := VehicleSpawner.AgentEntersVehicleEvent.Await()
Logger.Print("Player entered the vehicle")
set MaybePlayer = option{player[VehiclePlayer]}
StartGame()
HandlePlayerExitsVehicle<private>(VehiclePlayer : agent) : void =
Logger.Print("Player exited the vehicle. Reassigning player to vehicle")
VehicleSpawner.AssignDriver(VehiclePlayer)
StartGame<private>()<suspends> : void =
Logger.Print("Trying to start the game...")
<# Başlatıldığında InitialCountdownTime değerinden geri sayacak yeni bir countdown_timer oluşturuyoruz.
Ayrıca oyuncunun puanını ve alma seviyesini takip edecek yeni bir score_manager oluştur.
countdown_timer ve score_manager arayüzün gösterileceği bir oyuncu bulunmasını gerektirir.
Şu ana kadar araca giren, oyun başlangıcını tetikleyen geçerli bir oyuncuya sahip olmalıyız. #>
if (ValidPlayer := MaybePlayer?):
Logger.Print("Valid player, starting game...")
set ScoreManager = MakeScoreManager(ValidPlayer, ScoreManagerDevice)
ScoreManager.AddScoreManagerToUI()
set CountdownTimer = MakeCountdownTimer(InitialCountdownTime, ValidPlayer)
CountdownTimer.StartCountdown()
# Geri sayımın sona ermesini bekleriz.
# Aynı zamanda, temel oynanışı oluşturan Alma ve Teslim oyun döngüsünü çalıştırırız.
race:
HandleCountdownEnd(ValidPlayer)
PickupDeliveryLoop()
else:
Logger.Print("Can't find valid player. Aborting game start", ?Level := log_level.Error)
HandleCountdownEnd<private>(InPlayer : player)<suspends> : void =
TotalTime := CountdownTimer.CountdownEndedEvent.Await()
ScoreManager.AwardScore()
EndGame.Activate(InPlayer)
PickupDeliveryLoop<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(){}
<# Defer, MapIndicator cihazını devre dışı bırakır, böylece PickupDeliveryLoop işlevinin sonlandırılması her zaman işaretin devre dışı bırakılmasıyla sonuçlanır.
Defer ayrıca, PickupDeliveryLoop iptal edilirse de yürütülür. #>
defer:
if (ValidPlayer := MaybePlayer?):
PickupMarker.MapIndicator.DeactivateObjectivePulse(ValidPlayer)
PickupMarker.MapIndicator.Disable()
PickupMarker.MapIndicator.Enable()
loop:
var PickupLevel : int = 0
var IsFirstPickup : logic = true
<# Döngü her başladığında ScoreManager aracılığıyla alma seviyesi kullanıcı arayüzünü sıfırlamalıyız.
Kullanıcı Arayüzündeki alma seviyesi 1’den başlar (0’dan değil). 0’dan başlarsa bazı oyuncuların aklı karışabilir.
Dizini oluşturmaya 0’dan başlarız. Bu yüzden PickupLevel=0, Kullanıcı Arayüzündeki 1. Seviyedir. #>
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)
<# Etkinleştirdiğimiz PickupZone için gereken tek geciktirme (defer) budur. Her dış döngünün sonunda ilk PickupZone bölgesini devre dışı bırakacak
veya sonraki herhangi bir PickupZone’u devre dışı bırakacaktır. Bunun nedeni, ifadenin en sonda PickupZone değişkeni daha yeni bir bölgeye bağlandığında değerlendirilmesidir. #>
defer:
PickupZone.DeactivateZone()
PickupZone.ZoneCompletedEvent.Await()
Logger.Print("Picked up", ?Level:=log_level.Normal)
<# Pizzaların üst üste yığılmasını ve yığın dolduktan sonra yere düşmesini önlemek için pizza alımlarını oyuncu çantasından kaldırıyoruz. #>
if (RemovingPlayer := MaybePlayer?):
PizzaRemover.Remove(RemovingPlayer)
# İlk alımdan sonra teslim bölgesini etkinleştirebiliriz.
if (IsFirstPickup?):
set IsFirstPickup = false
FirstPickupZoneCompletedEvent.Signal()
if (PickupPoints := PointsForPickupLevel[PickupLevel]):
ScoreManager.UpdatePendingScore(PickupPoints)
# Alma seviyesini ve ScoreManager değerini güncelle.
if (PickupLevel < MaxPickupLevel):
set PickupLevel += 1
ScoreManager.UpdatePickupLevel(PickupLevel + 1)
else:
Logger.Print("Seçilecek bir sonraki PickupZone bulunamıyor.", ?Level := log_level.Error)
return # PickupDeliveryLoop döngüsünden doğan hata
block:
FirstPickupZoneCompletedEvent.Await()
if (DeliveryZone := DeliveryZoneSelector.SelectNext[]):
DeliveryZone.ActivateZone()
# PickupDeliveryLoop döngüsünün iptal edildiğinde tüm etkin teslim bölgelerini de devre dışı bırakarak sonlanması için bölge devre dışı bırakmayı geciktiriyoruz.
defer:
Logger.Print("Deactivating delivery zone.", ?Level:=log_level.Normal)
DeliveryZone.DeactivateZone()
DeliveryZone.ZoneCompletedEvent.Await()
Logger.Print("Delivered", ?Level:=log_level.Normal)
PointsCommitted := ScoreManager.AddPendingScoreToTotalScore()
BonusTime : float = DeliveryBonusSeconds * PointsCommitted
CountdownTimer.AddRemainingTime(BonusTime)
else:
Logger.Print("Seçilecek bir sonraki DeliveryZone bulunamıyor.", ?Level:=log_level.Error)
return # PickupDeliveryLoop döngüsünden doğan hata
pickup_delivery_zone.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 }
<# Bölge, Etkinleştirilebilen/Devre Dışı Bırakılabilen ve bölge "Tamamlandı" duruma geldiğinde
(bir sonraki etkinleştirmeye kadar bir daha tamamlanamaz) sinyal veren olaylar sağlayan bir harita alanıdır (bir cihaz ile gösterilir).
"Tamamlandı" bölgesi, bölgenin cihaz türüne (ActivatorDevice) bağlıdır.
Önerilen kullanım: ActivateZone() -> ZoneCompletedEvent.Await() -> DeactivateZone() #>
base_zone<public> := class:
ActivatorDevice<public> : creative_object_interface
ZoneCompletedEvent<public> : event(base_zone) = event(base_zone){}
GetTransform<public>() : transform =
ActivatorDevice.GetTransform()
<# Bölgeyi etkinleştirir.
Bölge için cihazları ve her türlü görsel göstergeyi buradan etkinleştirmelisin. #>
ActivateZone<public>() : void =
# Temel bölge, eşya oluşturma yeri veya ele geçirme alanı olarak tanımlanan bölgeleri işleyebilir.
# Hangi bölge üzerinde işlem yaptığımızı görmek için her bir türe dönüştürmeyi dene.
if (CaptureArea := capture_area_device[ActivatorDevice]):
CaptureArea.Enable()
spawn { WaitForZoneCompleted(option{CaptureArea.AgentEntersEvent}) }
else if (ItemSpawner := item_spawner_device[ActivatorDevice]):
ItemSpawner.Enable()
spawn { WaitForZoneCompleted(option{ItemSpawner.ItemPickedUpEvent}) }
<# Bölgeyi devre dışı bırakır.
Bölge için cihazları ve her türlü görsel göstergeyi buradan devre dışı bırakmalısın. #>
DeactivateZone<public>() : void =
if (CaptureArea := capture_area_device[ActivatorDevice]):
CaptureArea.Disable()
else if (ItemSpawner := item_spawner_device[ActivatorDevice]):
ItemSpawner.Disable()
ZoneDeactivatedEvent.Signal()
<# Bölge tamamlanmadan devre dışı bırakılırsa WaitForZoneCompleted eş yordamını sonlandırmak için bu olay gereklidir. #>
ZoneDeactivatedEvent<protected> : event() = event(){}
WaitForZoneCompleted<private>(ZoneDeviceCompletionEventOpt : ?awaitable(agent))<suspends> : void =
if (DeviceEvent := ZoneDeviceCompletionEventOpt?):
race:
block:
DeviceEvent.Await()
ZoneCompletedEvent.Signal(Self)
ZoneDeactivatedEvent.Await()
MakeBaseZone<constructor><public>(InActivatorDevice : creative_object_interface) := base_zone:
ActivatorDevice := InActivatorDevice
# tagged_zone_selector, InitZones’a geçirilen etiketlerin eklendiği tetikleyicilere göre bölgeler oluşturur.
tagged_zone_selector<public> := class:
var Zones<protected> : []base_zone = array{}
InitZones<public>(ZoneTag : tag) : void =
<# Bir bölge seçici oluşturulduğunda tüm kullanılabilir bölgeleri bul
ve bir sonraki bölgenin seçildiği her durumda etiketli cihazları ararken
zaman kaybetmemek için önbelleğe al. #>
ZoneDevices := GetCreativeObjectsWithTag(ZoneTag)
set Zones = for (ZoneDevice : ZoneDevices):
MakeBaseZone(ZoneDevice)
SelectNext<public>()<transacts><decides> : base_zone =
Zones[GetRandomInt(0, Zones.Length-1)]
score_manager.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:
<# Puan yöneticisi yaşam süresi boyunca tuvali yeniden oluşturmayacağımız için bu işlemi
bu türden bir obje oluşturulduğunda bir kez yapmalısın. #>
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()
<# PendingScore değerini TotalGameScore değerine ekler ve PendingScore değerini 0 yapar.
Eklenen toplam Alma Puanı sayısını döndürür. #>
AddPendingScoreToTotalScore<public>() : int =
set TotalGameScore += PendingScore
defer:
set PendingScore = 0
UpdateUI()
PendingScore
<# Belirtilen miktarda puanı beklemedeki puanlara ekler. #>
UpdatePendingScore<public>(Points : int) : void =
set PendingScore += Points
UpdateUI()
UpdatePickupLevel<public>(Level : int) : void =
set PickupLevel = Level
UpdateUI()
<# ScoreManagerDevice cihazını etkinleştirerek puanı oyuncuya verir. #>
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 = "Alma Seviyesi: {InLevel}"
PendingScoreText<private><localizes>(InPoints : int) : message = "Bekleyen Puanlar: {InPoints}"
TotalGameScoreText<private><localizes>(InPoints : tamsayı) : mesaj = "Total Points: {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))
Kendi Kendine Yapabileceklerin
Bu kılavuzu tamamlayarak tam zamana karşı yarış oyunu Pizza Pursuit’i oluşturmak için nasıl Verse kullanabileceğini öğrendin.
Öğrendiklerini kullanarak aşağıdakileri dene:
- Daha fazla alma bölgesi seviyesi ekle.
- Farklı türde teslim bölgeleri ekle. Oyuncunun bölgeyi tamamlamak için buton gibi başka bir cihazı etkinleştirmesini gerekli kılmak için
base_zone
sınıfını genişlet. - Oyuncuyu arabadan çıkar ve pizzayı teslim etmek için kısa bir engelli parkuru tamlamasını sağla.
- Aynı anda birden fazla bölgeyi etkinleştir.
- Oyuncudan uzaklığa göre bölge seçme ölçütlerini ayarla.