Oyun döngüsü; girdiye (genellikle oyun kumandası veya fare ile etkileşime giren oyuncu) yanıt vermek, oyun durumunu güncellemek ve bir butona basıldığında ışığın açılması gibi durumlarda oyuncuya oyun durumunu etkilediğini gösteren çıktılar sağlamak amacıyla yinelenerek (döngü halinde) çalışan bir koddur. Genellikle oyuncunun bir hedefe ulaşması ya da hedefe ulaşılamadan sürenin dolmasına benzer bir hata durumu gibi hallerde oyun bir tamamlanma durumuna ulaştığında döngü sona erer.
Time Trial: Pizza Pursuit eğitimindeki bu adımı tamamlayarak oyun döngüsünü nasıl oluşturacağını ve oyunun tamamlama ve hata durumlarını nasıl tanımlayacağını öğreneceksin.
Aşağıda, Time Trial: Pizza Pursuit’in oyun döngüsü için sözde kod yer almaktadır:
loop:
race:
loop:
SelectNextPickupZone
WaitForPlayerToCompletePickupZone
block:
WaitForFirstPickup
SelectNextDeliveryZone
WaitForPlayerToCompleteDeliveryZone
Bu döngü, geri sayım bittiğinde veya oyunda beklenmeyen bir hata varsa sona erer.
Temel Oyun Döngüsünü Oluşturma
game_coordinator_device.verse dosyasını güncellemek için aşağıdaki adımları izle:
privatevesuspendsbelirleyicilerine sahipPickupDeliveryLoop()adlı yeni bir metot oluştur. Daha önceOnBegin()içinde oluşturulan döngüyü bu yeni metoda taşı.VerseOnBegin<override>;()<suspends> : void = SetupZones() PickupDeliveryLoop() PickupDeliveryLoop<private>()<suspends> : void = var PickupLevel : int = 0 loop: if (PickupZone : base_zone = PickupZoneSelectors[PickupLevel].SelectNext[]):Etiket dizisinin uzunluğundan alma seviyelerinin maksimum sayısını belirle ve alma seviyesi maksimum alma seviyesi sayısından büyük olmadığı sürece oyuncu bir alma bölgesini her tamamladığında
PickupLeveldeğerini artır.VerseOnBegin<override>;()<suspends> : void = SetupZones() PickupDeliveryLoop() 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 var PickupLevel : int = 0Teslim bölgesi, oyuncu ilk alımını tamamladıktan sonra etkileşir ancak oyuncu teslim bölgesine gitmeden önce isterse yine eşya alabilecektir. Bunu yapmak için alma bölgesi kodunun ve teslim bölgesi kodunun aynı zamanda gerçekleşmesi gerekir. Bu örnekte race eşzamanlılık ifadesi kullanılıyor çünkü:
Oyuncu bir teslimatı tamamladığında teslim bloku, alma bölgesi döngüsünü iptal eder.
Alma döngüsüyle ilgili bir sorun varsa alma bölgesi döngüsü, teslim blokunu iptal eder.
Ayrıca bölgeyi devre dışı bırakma işleminde ufak bir değişiklik yapman gerekir. Döngünün veya teslim blokunun iptal edilmesi durumunda kod bölgenin tamamlanmasını bekliyorsa
DeactivateZone()çağrılmamalıdır.Bölge devre dışı bırakma satırı hiçbir zaman yürütülmeyeceği için bölge etkin kalarak bir hata oluşturur.
Bunu düzeltmek için defer ifadesini kullanabilirsin.
defer,deferifadesinin göründüğü kapsam sona erene kadar kendi içerdiği ifadelerin yürütülmesini erteler.defer; bir kapsamdan normal şekilde ayrılma (fonksiyonun sonu), erken çıkma (dönüş veya kesinti gibi) veya iptal edilen herhangi bir eşzamanlı görev/asenkron ifade (örneğinrace) gibi nedenlerle program kontrolü kapsam dışına aktarıldıktan sonra çalışır. Bu durum, ne olursa olsun en sonda yürütülecek işlemleri kuyruğa almaya benzer. HerDeactivateZoneçağrısını birdeferifadesinde topla ve ilgiliZoneCompletedEvent.Await()fonksiyonunun önüne taşı.VersePickupDeliveryLoop<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 var PickupLevel : int = 0 race: loop: if (PickupZone : base_zone = PickupZoneSelectors[PickupLevel].SelectNext[]): PickupZone.ActivateZone()Önceki örnek, teslim bölgesini alma bölgesiyle aynı zamanda etkinleştirir ancak teslim bölgesinin etkinleşmesi ilk alım tamamlanana kadar beklemelidir. Bunu yapmak için bir olay ekle ve teslim bölgesini etkinleşmeden önce olay için beklet.
VerseOnBegin<override>;()<suspends> : void = SetupZones() PickupDeliveryLoop() 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(){}Oyuncunun sürekli olarak eşya alabilmesi ve teslim edebilmesi için oyun bitene kadar bu alma bölgesi/teslim bölgesi yarış ifadesini döngüye al.
VerseOnBegin<override>;()<suspends> : void = SetupZones() PickupDeliveryLoop() 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(){}game_coordinator_device.verse dosyan şu anda böyle görünmelidir:
Verseusing { /Verse.org/Simulation } using { /Fortnite.com/Devices } using { /Fortnite.com/Vehicles } using { /Fortnite.com/Characters } using { /Fortnite.com/Playspaces } using { /Verse.org/Random } using { /UnrealEngine.com/Temporary/Diagnostics } using { /UnrealEngine.com/Temporary/SpatialMath } using { /UnrealEngine.com/Temporary/Curves } using { /Verse.org/Simulation/Tags }
Verse dosyalarını kaydet, kodunu derle ve bölümüne oynanış testi uygula.
Bölümüne oynanış testi uyguladığında oyunun başlangıcında ve oyuncu bir eşya aldıktan sonra Eşya Oluşturma Yeri cihazlarından biri etkinleşir. Oyuncu ilk eşyasını aldıktan sonra bu Eşya Oluşturma Yeri cihazı devre dışı kalır ve sonra bir Ele Geçirme Alanı cihazı etkinleşir. Bu döngü sen oyunu manuel olarak sonlandırana kadar devam eder.
Oyun Döngüsü İçin Tamamlama ve Başarısızlık Durumları Tanımlama
Temel oyun döngüsünü oluşturduktan sonra oyun döngüsünün tamamlama ve hata durumunu tanımlamalısın. Bu oyunun aşağıdaki koşullarda sona ermesi gerekir:
Bir geri sayım bittiğinde veya
Oyun döngüsüyle ilgili bir sorun olduğunda.
Oyunun tamamlama ve hata durumlarını ayarlamak için şu adımları izle:
game_coordinator_deviceiçindeprivatebelirleyicisine sahip bir countdown_timer sınıfı örneği oluştur.game_coordinator_device<public> := class(creative_device): @editable EndGame<public> : end_game_device = end_game_device{} var CountdownTimer<private> : countdown_timer = countdown_timer{}Versegame_coordinator_device<public> := class(creative_device): @editable EndGame<public> : end_game_device = end_game_device{} var CountdownTimer<private> : countdown_timer = countdown_timer{}countdown_timeroluşturucusu için bir oyuncu referansı gerektiğinden, bu tek oyunculu oyunda bir oyuncu referansı kaydetmek için isteğe bağlı bir oyuncu değişkeni ekle ve oyuncu referansını almak içinFindPlayer()adlı bir fonksiyon oluştur. Bölgeleri ayarlamadan önceOnBegin()içindeFindPlayer()çağrısı yap.Versegame_coordinator_device<public> := class(creative_device): @editable EndGame<public> : end_game_device = end_game_device{} var CountdownTimer<private> : countdown_timer = countdown_timer{} var MaybePlayer<private> : ?player = false OnBegin<override>()<suspends> : void = FindPlayer() SetupZones() FindPlayer<private>() : void = # Since this is a single player experience, the first player (at index 0) # should be the only one available. if (FirstPlayer := GetPlayspace().GetPlayers()[0]): set MaybePlayer = option{FirstPlayer} Logger.Print("Player found") else: # Log an error if we can't find a player. # This shouldn't happen because at least one player is always present. Logger.Print("Can't find valid player", ?Level := log_level.Error)Geri sayım süreölçerinin bitmesini bekleyen ve Oyunu Sonlandırma cihazını etkinleştiren
HandleCountdownEnd()adlı bir cihaz oluştur.VerseHandleCountdownEnd<private>(InPlayer : agent)<suspends> : void = CountdownTimer.CountdownEndedEvent.Await() EndGame.Activate(InPlayer)StartGame()adlı bir fonksiyon oluştur ve bu fonksiyonuOnBegin()içindeSetupZones()fonksiyonundan sonra çağır. Bu fonksiyon şunları yapmalıdır:Geri sayım süreölçerini başlat.
Versegame_coordinator_device<public> := class(creative_device): # How long the countdown timer will start counting down from. @editable InitialCountdownTime<public> : float = 30.0 @editable EndGame<public> : end_game_device = end_game_device{} OnBegin<override>()<suspends> : void = FindPlayer() SetupZones() StartGame() StartGame<private>()<suspends> : void = Logger.Print("Trying to start the game...") <# We construct a new countdown_timer that'll countdown from InitialCountdownTime once started. The countdown_timer requires a player to show their UI to. We should have a valid player by now. #> if (ValidPlayer := MaybePlayer?): Logger.Print("Valid player, starting game...") set CountdownTimer = MakeCountdownTimer(InitialCountdownTime, ValidPlayer) CountdownTimer.StartCountdown() else: Logger.Print("Can't find valid player. Aborting game start", ?Level := log_level.Error)raceifadesini kullanarak hemHandleCountdownEnd(ValidPlayer)hem dePickupDeliveryLoop()fonksiyonunu çağır. Böylece:Geri sayım sona erdiğinde oyun döngüsü durur veya
Oyun döngüsü durursa geri sayım iptal edilir.
VerseStartGame<private>()<suspends> : void = Logger.Print("Trying to start the game...") <# We construct a new countdown_timer that'll countdown from InitialCountdownTime once started. The countdown_timer requires a player to show their UI to. We should have a valid player by now. #> if (ValidPlayer := MaybePlayer?): Logger.Print("Valid player, starting game...") set CountdownTimer = MakeCountdownTimer(InitialCountdownTime, ValidPlayer) CountdownTimer.StartCountdown() # We wait for the countdown to end. # At the same time, we also run the Pickup and Delivery game loop that constitutes the core gameplay. race: HandleCountdownEnd(ValidPlayer) PickupDeliveryLoop() else: Logger.Print("Can't find valid player. Aborting game start", ?Level := log_level.Error)
game_coordinate_device.verse dosyan şu anda böyle görünmelidir:
Verseusing { /Verse.org/Simulation } using { /Fortnite.com/Devices } using { /Fortnite.com/Vehicles } using { /Fortnite.com/Characters } using { /Fortnite.com/Playspaces } using { /Verse.org/Random } using { /UnrealEngine.com/Temporary/Diagnostics } using { /UnrealEngine.com/Temporary/SpatialMath } using { /UnrealEngine.com/Temporary/Curves } using { /Verse.org/Simulation/Tags }Verse dosyalarını kaydet, kodunu derle ve bölümüne oynanış testi uygula.
Bölümüne oynanış testi uyguladığında oyun önceki bölümdekiyle aynı şekilde çalışır ancak artık geri sayım sona erdiğinde veya oyun döngüsünde bir sorun olduğunda sona erecek bir süreölçer vardır.