Verse kodunda her şey beklediğin gibi çalışmadığında neyin yanlış gittiğini anlaman bazen zor olabilir. Örneğin, aşağıdakilerle karşılaşabilirsin:
Çalışma zamanı hataları.
Kodun yanlış sırada yürütülmesi.
İşlemlerin olması gerekenden uzun sürmesi.
Bunlardan herhangi biri, kodunun beklenmeyen şekillerde davranmasına neden olabilir ve deneyiminde sorunlar yaratabilir. Kodundaki sorunları tanılama eylemine hata ayıklama adı verilmekte olup kodunu düzeltmek ve iyileştirmek için kullanabileceğin birkaç farklı çözüm vardır.
Verse Çalışma Zamanı Hataları
Verse kodun, hem sen onu yazdığın sırada dil sunucusunda hem de sen kodu editörden veya Visual Studio Code’dan derlediğinde analiz edilir. Ancak bu semantik analiz tek başına, karşılaşabileceğin olası tüm sorunları yakalayamaz. Kodun, çalışma zamanında yürütüldüğünde çalışma zamanı hatalarını tetikleyebilirsin. Bu hatalar, sonraki tüm Verse kodlarının yürütülmesinin durdurulmasına neden olacaktır. Bunun sonucunda da deneyimin oynanamaz hale gelebilir.
Örnek olarak, aşağıdakileri yapan bir Verse kodun olduğunu varsayalım:
# Has the suspends specifier, so can be called in a loop expression.
SuspendsFunction()<suspends>:void={}
# Calls SuspendFunction forever without breaking or returning,
# causing a runtime error due to an infinite loop.
CausesInfiniteLoop()<suspends>:void=
loop:
SuspendsFunction()CausesInfiniteLoop() fonksiyonu, Verse derleyicisinde herhangi bir hataya neden olmaz ve programın başarıyla derlenir. Ancak CausesInfiniteLoop()’u çalışma zamanında çağırırsan bu fonksiyon sonsuz bir döngü çalıştıracak, dolayısıyla da bir çalışma zamanı hatasını tetikleyecektir.
Deneyiminde meydana gelen çalışma zamanı hatalarını incelemek için İçerik Hizmeti Portalı’na git. Burada, yayınlanmış ve yayınlanmamış tüm projelerinin bir listesini görebilirsin. Her proje için, bir projede meydana gelen çalışma zamanı hatalarının kategorilerini listeleyen bir Verse sekmesine erişimin vardır. Hatanın raporlandığı Verse çağrı yığınına da göz atabilirsin; burada neyin yanlış gitmiş olabileceği konusunda daha ayrıntılı bilgiler verilmektedir. Hata raporları en fazla 30 gün boyunca depolanır.
Bunun, henüz geliştirme aşamasının başlarında olan yeni bir özellik olduğunu ve gelecekteki UEFN ve Verse sürümlerinde bu özelliğin işleyişinin değişebileceğini lütfen unutma.
Yavaş Kodun Profilini Oluşturma
Kodun beklediğinden daha yavaş çalışıyorsa profile ifadesini kullanarak kodu test edebilirsin. Profile ifadesi sana, belirli bir kod parçasının çalışmasının ne kadar zaman aldığını söyler ve yavaş kod bloklarını belirleyip iyileştirmene yardımcı olabilir. Örneğin, bir dizinin belirli bir sayıyı içerip içermediğini öğrenmek ve göründüğü yerdeki dizini döndürmek istediğini varsayalım. Bunu, dizi genelinde yineleme yaparak ve sayının aradığın sayıyla eşleşip eşleşmediğini kontrol ederek yapabilirsin.
# An array of test numbers.
TestNumbers:[]int = array{1,2,3,4,5}
# Runs when the device is started in a running game
OnBegin<override>()<suspends>:void=
# Find if the number exists in the TestNumbers array by iterating
# through each element and checking if it matches.
for:
Index -> Number:TestNumbers
Ancak bu kod, dizinin her bir sayısını eşleşme açısından kontrol etmesi gerektiğinden verimsizdir. Kod, öğeyi bulsa bile listenin geri kalanını kontrol etmeye devam edeceğinden bu durum, verimsiz bir zaman karmaşıklığına yol açacaktır. Bu kod yerine, Find[] fonksiyonunu kullanarak dizinin aradığın sayıyı içerip içermediğini kontrol edip onu döndürebilirsin. Find[], öğeyi bulduğunda hemen döndürdüğünden öğe listeye ne kadar erken girdiyse o kadar hızlı yürütülecektir. Her iki kod parçasını da test etmek için bir profile ifadesi kullanırsan bu durumda Find[] fonksiyonunu kullanan kodun, yürütme süresinin azalmasını sağladığını göreceksin.
# Runs when the device is started in a running game
OnBegin<override>()<suspends>:void=
# Find if the number exists in the TestNumbers array by iterating
# through each element and checking if it matches.
profile("Finding a number by checking each array element"):
for:
Index -> Number:TestNumbers
Number = 4
do:
Yürütme süresindeki bu küçük farklılıklar, yinelemen gereken öğe sayısı arttıkça büyür. Büyük bir listeyi yinelerken yürüttüğün her ifade, özellikle dizilerin yüzlerce, hatta binlerce öğeye ulaştıkça zaman karmaşıklığına katkıda bulunur. Deneyimlerini giderek daha fazla oyuncuya yönelik olarak ölçeklendirdikçe yavaşlama yaşanan temel alanları bulup bunlarla mücadele etmek için profile ifadesini kullan.
Günlükçüler ve Çıktıların Günlüğe Kaydedilmesi
Varsayılan olarak, Verse kodunda bir mesajı yazdırmak için Print() fonksiyonunu çağırdığında bu mesaj özel bir Print günlüğüne yazılır. Yazdırılan mesajlar oyun içi ekranda, oyun içi günlükte ve UEFN’deki Çıktı Günlüğü’nde görünür.
Bir mesajı Print() fonksiyonunu kullanarak yazdırdığında, bu mesaj Çıktı Günlüğü’ne, oyun içi Günlük sekmesine ve oyun içi ekrana yazılır.
Ancak mesajların oyun içi ekranda görünmesini istemeyebileceğin birçok durum vardır. Bir şeylerin ne zaman olduğunu takip etmek (örneğin, bir olay tetiklendiğinde veya belirli bir süre geçtiğinde) veya kodunda bir şeyler ters gittiğinde bunun sinyalinin verilmesi için mesajları kullanmak isteyebilirsin. Oyun sırasında birden fazla mesaj, özellikle de oyuncuya ilgileneceği bilgiler sağlamıyorsa dikkat dağıtıcı olabilir.
Bu sorunu çözmek için bir günlük kaydedici kullanabilirsin. Günlük kaydedici, mesajları ekranda görüntülemeden doğrudan Çıktı Günlüğü’ne ve Günlük sekmesine yazdırmana olanak tanıyan özel bir sınıftır.
Günlük Kaydedicileri
Bir günlük kaydedici oluşturmak için öncelikle bir günlük kanalı oluşturman gerekir. Her günlükçü çıktı günlüğüne mesaj yazdırır, ancak hangi mesajın hangi günlükçüden geldiğini ayırt etmek zor olabilir. Günlük kanalları, mesajın başlangıcına günlük kanalının adını ekleyerek mesajı hangi günlükçünün gönderdiğinin görülmesini kolaylaştırır. Günlük kanalları modül kapsamında, günlük kaydediciler ise sınıf veya fonksiyonlar içinde bildirilir. Aşağıda, modül kapsamında bir günlük kanalı bildirilmesi, ardından da bir Verse cihazının içindeki bir günlükçünün bildirilmesi ve çağrılmasına dair bir örnek yer almaktadır.
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
# A log channel for the debugging_tester class.
# Log channels declared at module scope can be used by any class.
debugging_tester_log := class(log_channel){}
# A Verse-authored creative device that can be placed in a level
debugging_tester := class(creative_device):
Bir mesajı, günlük kaydedicinin Print() fonksiyonunu kullanarak yazdırdığında, bu mesaj Çıktı Günlüğü’ne ve oyun içi Günlük sekmesine yazılır.
Günlük Seviyeleri
Kanallara ek olarak, günlük kaydedicinin yazdırma için kullanacağı varsayılan bir günlük seviyesi de belirtebilirsin. Her biri kendi özelliklerine sahip olan beş seviye vardır:
| Günlük Seviyesi | Şuraya Yazdır: | Özel Özellikler |
|---|---|---|
Hata ayıklama | Oyun içi günlük | Yok |
Ayrıntılı | Oyun içi günlük | Yok |
Normal | Oyun içi günlük, Çıktı Günlüğü | Yok |
Uyarı | Oyun içi günlük, Çıktı Günlüğü | Metin rengi sarı |
Hata | Oyun içi günlük, Çıktı Günlüğü | Metin rengi kırmızı |
Bir günlük kaydedici oluşturduğunda, varsayılan olarak Normal günlük seviyesi kullanılır. Günlük kaydedici oluştururken günlük kaydedicinin seviyesini değiştirebilir veya Print() fonksiyonunu çağırırken yazdırılacak bir günlük seviyesi belirtebilirsin.
# A logger local to the debugging_tester class. By default, this prints
# to log_level.Normal.
Logger:log = log{Channel := debugging_tester_log}
# A logger with log_level.Debug as the default log channel.
DebugLogger:log = log{Channel := debugging_tester_log, DefaultLevel := log_level.Debug}
# Runs when the device is started in a running game
OnBegin<override>()<suspends>:void=
Yukarıdaki örnekte, Logger varsayılan olarak Normal günlük kanalını kullanırken DebugLogger varsayılan olarak Debug günlük kanalını kullanır. Herhangi bir günlük kaydedici, Print()’i çağırırken log_level’ı belirterek herhangi bir günlük seviyesine yazdırabilir.
Farklı günlük seviyelerine yazdırmak için günlük kaydedici kullanmanın sonuçları. log_level.Debug ve log_level.Verbose öğelerinin oyun içi günlüğe değil, yalnızca UEFN Çıktı Günlüğü’ne yazdırıldığını unutma.
Çağrı Yığınını Yazdırma
Çağrı yığını, geçerli kapsama yol açan fonksiyon çağrılarının listesini takip eder. Çağrı yığını, kodunun, geçerli yordamın yürütülmesi bittiğinde nereye dönmesi gerektiğini bilmek için kullandığı, yığılmış bir talimatlar dizisi gibidir. Çağrı yığınını, PrintCallStack() fonksiyonunu kullanarak herhangi bir günlük kaydediciden yazdırabilirsin. Örneğin, aşağıdaki koda bakalım:
# A logger local to the debugging_tester class. By default, this prints
# to log_level.Normal.
Logger:log = log{Channel := debugging_tester_log}
# Runs when the device is started in a running game
OnBegin<override>()<suspends>:void=
# Move into the first function, and print the call stack after a few levels.
LevelOne()
Yukarıdaki OnBegin() içindeki kod, birinci fonksiyona geçmek için LevelOne()’ı çağırır. Ardından LevelOne(), LevelTwo()’yu, LevelTwo(), LevelThree()’yi, LevelThree() de geçerli çağrı yığınını yazdırmak için Logger.PrintCallStack()’i çağırır. En son çağrı, yığının en üstünde olacağı için ilk olarak LevelThree() yazdırılacaktır. Ardından, bu sırayla LevelTwo(), LevelOne() ve OnBegin() fonksiyonlarını uygula.
Kodunda bir şeyler ters gittiğinde, o noktaya gelinmesine tam olarak hangi çağrıların yol açtığını bilmek için çağrı yığınını yazdırmak yararlı olur. Çağrı yığınını yazdırman, kodun yapısını, çalıştığı sırada görmeni kolaylaştırır ve kodun yoğun olduğu projelerde bireysel yığın izlemelerini ayrıştırabilmen için sana bir yol sunar.
Hata Ayıklama Çizimi ile Oyun Verilerini Görselleştirme
Deneyimlerinin farklı özelliklerinde hata ayıklamanın bir başka yolu da Hata Ayıklama Çizimi API’sini kullanmaktır. Bu API, oyun verilerini görselleştirmek için hata ayıklama şekilleri oluşturabilir. Bazı örnekler şunlardır:
Bir muhafızın görüş alanı.
Nesne yürütücünün bir objeyi yürüteceği mesafe.
Ses çaların zayıflama mesafesi.
Bu verileri yayınlanmış bir deneyimde kullanıma açmadan deneyiminde ince ayar yapmak için bu hata ayıklama şekillerini kullanabilirsin. Daha fazla bilgi için Verse’te Hata Ayıklama Çizimi bölümüne bakabilirsin.
Eşzamanlılık ile İyileştirme ve Zamanlama
Eşzamanlılık, Verse programlama dilinin ana unsurlarından biridir ve deneyimlerini geliştirecek güçlü bir araçtır. Eşzamanlılık ile bir Verse cihazının aynı anda birden fazla işlem çalıştırmasını sağlayabilirsin. Böylece daha esnek ve kompakt kod yazabilir ve bölümünde kullanılan cihaz sayısından tasarruf edebilirsin. Eşzamanlılık, iyileştirme için harika bir araçtır ve birden fazla görevi aynı anda yürütmek için asenkron kod kullanmanın yollarını bulmak, programlarında yürütmeyi hızlandırmak ve zamanlamayla ilgili sorunları çözmek için harika bir yoldur.
Çıkma ile Asenk. Bağlamlar Oluşturma
spawn ifadesi, herhangi bir bağlamdan asenkron bir ifade başlatır ve aynı zamanda söz konusu asenkron ifadeyi takip eden ifadelerin hemen yürütülmesine olanak tanır. Böylece birden fazla görev, bunların her biri için yeni Verse dosyaları oluşturulmasına gerek kalmadan, aynı anda, aynı cihazdan çalıştırılabilir. Örneğin, her oyuncunun canını saniyede bir izleyen bir kodun olduğu bir senaryoyu düşünelim. Bir oyuncunun canı belirli bir sayının altına düşerse onu küçük bir miktar kadar iyileştirmen gerekir. Ardından, başka bir görevi yerine getiren bir kod çalıştırman gerekecektir. Bu kodu uygulayan bir cihaz aşağıdaki gibi görünebilir:
# A Verse-authored creative device that can be placed in a level
healing_device := class(creative_device):
# Runs when the device is started in a running game
OnBegin<override>()<suspends>:void=
AllPlayers:[]agent = GetPlayspace().GetPlayers()
# Every second, check each player. If the player has less than half health,
# heal them by a small amount.
Ancak bu döngü sonsuza kadar çalıştığı ve hiç bozulmadığı için onu takip eden hiçbir kod kesinlikle çalışmayacaktır. Bu sınırlayıcı bir tasarımdır, çünkü bu cihaz yalnızca döngü ifadesini çalıştırma işine takılıp kalmıştır. Cihazın aynı anda birden fazla şey yapmasına ve eşzamanlı olarak kod çalıştırmasına olanak tanımak için loop kodunu asenkron bir fonksiyona taşıyabilir ve bunu OnBegin() sırasında çıkarabilirsin.
# Runs when the device is started in a running game
OnBegin<override>()<suspends>:void=
# Use the spawn expression to run HealMonitor() asynchronously.
spawn{HealMonitor()}
# The code after this executes immediately.
Print("This code keeps going while the spawned expression executes")
HealMonitor(Players:[]agent)<suspends>:void=
Bu bir gelişmedir çünkü cihaz artık HealMonitor() fonksiyonu çalışırken başka bir kod çalıştırabilir. Ancak, yine de fonksiyonun her oyuncuda döngüden geçmesi gerekir ve deneyimdeki oyuncu sayısı arttıkça olası zamanlama sorunları oluşabilir. Örneğin, her oyuncuya CP’sine göre puan vermek veya oyuncunun elinde bir eşya olup olmadığını kontrol etmek isteseydin ne olurdu? for ifadesinde ekstra oyuncu başına mantık eklenmesi, bu fonksiyonun zaman karmaşıklığını artırır ve yeterli sayıda oyuncu olduğunda, bu oyunculardan biri hasar alırsa zamanlama sorunları nedeniyle zamanında iyileşemeyebilir.
Her oyuncuyu döngüden geçirerek ayrı ayrı kontrol etmek yerine, her oyuncu için fonksiyonun bir örneğini çıkararak bu kodu daha da iyileştirebilirsin. Bu, tek bir fonksiyonun tek bir oyuncuyu izleyebileceği anlamına gelir. Böylece kodun, iyileştirmeye ihtiyaç duyan oyuncuya geri döngü yapmadan önce her bir oyuncuyu kontrol etmek zorunda kalmaz. spawn gibi eşzamanlılık ifadelerini kendi yararına kullanman, kodunu daha verimli ve esnek hale getirebilir ve kod tabanında diğer görevlerin yerine getirilebilmesi için daha fazla alan kalır.
# Runs when the device is started in a running game
OnBegin<override>()<suspends>:void=
AllPlayers := GetPlayspace().GetPlayers()
# Spawn an instance of the HealMonitor() function for each player.
for:
Player:AllPlayers
do:
# Use the spawn expression to run HealMonitorPerPlayer() asynchronously.
spawn ifadesinin bir loop ifadesi içinde kullanılması, bu kullanımın doğru olmayan bir şekilde yürütülmesi halinde istenmeyen davranışlara neden olabilir. Örneğin, HealMonitorPerPlayer() hiçbir zaman sonlanmadığından bu kod, bir çalışma zamanı hatası oluşana kadar sonsuz sayıda asenkron fonksiyon çıkarmaya devam edecektir.
# Spawn an instance of the HealMonitor() function for each player, looping forever.
# This will cause a runtime error as the number of asynchronous functions infinitely increases.
loop:
for:
Player:AllPlayers
do:
spawn{HealMonitorPerPlayer(Player)}
Sleep(0.0)Zamanlamayı Olaylarla Kontrol Etme
Kodunun her parçasının doğru şekilde senkronize olmasını sağlaman, özellikle de çok sayıda kodun aynı anda çalıştığı geniş kapsamlı çok oyunculu deneyimlerde zor olabilir. Kodunun farklı bölümleri, başka fonksiyonların veya kodların ayarlanmış bir sırada yürütülmesine ihtiyaç duyabilir ve bu durum, sıkı kontroller olmadığı sürece söz konusu bölümler arasında zamanlama sorunlarına yol açabilir. Örneğin, belirli bir süre için geri sayım yapan, ardından da kendisine iletilen oyuncuya, bu oyuncunun CP’sinin eşik değerden büyük olması halinde bir puan veren aşağıdaki fonksiyonu ele alalım.
CountdownScore(Player:agent)<suspends>:void=
# Wait for some amount of time, then award each player whose HP is above the threshold some score.
Sleep(CountdownTime)
if:
Character := Player.GetFortCharacter[]
PlayerHP := Character.GetHealth()
PlayerHP >= HPThreshold
then:
ScoreManager.Activate(Player)Bu fonksiyonda <suspends> değiştiricisi olduğundan, spawn() kullanarak bir örneğini her oyuncu için asenkron olarak çalıştırabilirsin. Ancak, bu fonksiyona dayalı diğer tüm kodların her zaman fonksiyon tamamlandıktan sonra çalışacağını garanti etmen gerekir. CountdownScore() tamamlandıktan sonra puan alan her bir oyuncuyu yazdırmak istersen ne yapmalısın? CountdownScore()’un yürütülmesinin aldığı süre kadar beklemek için OnBegin() içinde Sleep()’i çağırarak yapabilirsin ancak bu işlem, oyunun çalıştığı sırada zamanlama sorunlarına yol açabilir ve aynı zamanda, kodunda değişiklik yapmak istemen durumunda sürekli olarak güncellemen gereken yeni bir değişkeni devreye sokar. Dolayısıyla bu yöntemi uygulamak yerine özel olaylar oluşturabilir ve kodundaki olayların sırasını sıkı bir şekilde kontrol etmek için bu özel olaylar üzerinde Await()’i çağırabilirsin.
# Custom event to signal when the countdown finishes.
CountdownCompleteEvent:event() = event(){}
# Runs when the device is started in a running game
OnBegin<override>()<suspends>:void=
AllPlayers := GetPlayspace().GetPlayers()
# Spawn a CountdownScore function for each player
for:
Bu kod şimdi CountdownCompletedEvent() için sinyal gönderilmesini beklediğinden her oyuncunun puanını yalnızca CountdownScore()’un yürütülmesi bittikten sonra kontrol etmesi garantilenmiştir. Birçok cihazda, kodunun zamanlamasını kontrol etmek için Await()’i çağırabileceğin yerleşik olaylar vardır ve bunlardan kendi özel olaylarınla faydalanarak birkaç hareketli parça içeren karmaşık oyun döngüleri oluşturabilirsin. Örnek olarak Verse Başlangıç Şablonu, karakter hareketlerini kontrol etmek, kullanıcı arayüzünü güncellemek ve genel oyun döngüsünü bölümler arası geçişlerde yönetebilmek için birkaç özel olay kullanır.
Sync, Race ve Rush ile Birden Fazla İfadeyi İşleme
sync, race ve rush ifadelerinin tamamı, aynı anda birden fazla asenkron ifadeyi çalıştırabilmene olanak tanırken aynı zamanda bu ifadelerin yürütülmesi tamamlandığında farklı fonksiyonlar da yerine getirir. Bunların her birinden faydalanarak asenk. ifadelerinin her birinin yaşam süresini sıkı bir şekilde kontrol edebilir, böylece birden fazla farklı durumu işleyebilen daha dinamik bir kod elde edebilirsin.
Örneğin, rush ifadesini ele alalım. Bu ifade, birden fazla asenk. ifadeyi eşzamanlı olarak çalıştırır, ancak yalnızca ilk biten ifadenin değerini döndürür. Takımların bir görevi tamamlamasının gerekli olduğu ve ilk bitiren takımın, diğer oyuncular görevi tamamlamaya çalışırken onlara müdahale etmesine olanak tanıyan bir güçlendirme aldığı bir mini oyununuz olduğunu varsayalım. Her bir takımın görevi ne zaman tamamladığını takip etmek için karmaşık bir zamanlama mantığı yazabilir veya rush ifadesini kullanabilirsin. İfade, bitecek ilk asenk. ifadenin değerini döndürdüğünden kazanan takımı döndürecek, aynı zamanda da diğer takımları işleyen kodun çalışmaya devam etmesine olanak tanıyacaktır.
WinningTeam := rush:
# All three async functions start at the same time.
RushToFinish(TeamOne)
RushToFinish(TeamTwo)
RushToFinish(TeamThree)
# The next expression is called immediately when any of the async functions complete.
GrantPowerup(WinnerTeam)race ifadesi de aynı kuralları izler ancak asenk. bir ifade tamamlandığında diğer ifadeler iptal edilir. Böylece birden fazla asenk. ifadesinin yaşam süresini aynı anda sıkı bir şekilde kontrol etmen mümkün olur, hatta bunu sleep() ifadesiyle birleştirip ifadenin çalışmasını istediğin süreyi sınırlandırabilirsin. rush örneğini ele alalım ancak bu kez bir takım kazandığında mini oyunun hemen bitmesini istediğini varsayalım. Mini oyunun sonsuza kadar devam etmemesi için bir süreölçer eklemen de yerinde olacaktır. race ifadesi, yarışı kaybeden ifadelerin ne zaman iptal edileceğini anlamak için olayları veya diğer eşzamanlılık araçlarını kullanmana gerek kalmadan bunların her ikisini de yapmana olanak tanır.
WinningTeam := race:
# All four async functions start at the same time.
RaceToFinish(TeamOne)
RaceToFinish(TeamTwo)
RaceToFinish(TeamThree)
Sleep(TimeLimit)
# The next expression is called immediately when any of the async functions complete. Any other async functions are canceled.
GrantPowerup(WinnerTeam)Son olarak, sync ifadesi de birden fazla ifadenin yürütülmesinin bitmesini beklemene olanak tanıyarak devam etmeden önce her birinin tamamlandığını garanti eder. sync ifadesi asenk. ifadelerin her birinden gelen sonuçları içeren bir demet döndürdüğünden tüm ifadelerini çalıştırmayı tamamlayabilir ve bunların her birinden gelen verileri birbirlerinden ayrı bir şekilde değerlendirebilirsin. Şimdi mini oyun örneğine geri dönelim ve her takıma mini oyundaki performansına göre güçlendirme vermek istediğini varsayalım. sync ifadesi burada devreye girer.
TeamResults := sync:
# All three async functions start at the same time.
WaitForFinish(TeamOne)
WaitForFinish(TeamTwo)
WaitForFinish(TeamThree)
# The next expression is called only when all of the async expressions complete.
GrantPowerups(TeamResults)Birden fazla dizi öğesinde asenk. bir ifade çalıştırmak istiyorsan bu ifadelerin tamamının senkronize olmasını garantilemek için kullanışlı ArraySync() fonksiyonunu kullanabilirsin.
Bu eşzamanlılık ifadelerinin her biri kendi başına güçlü bir araçtır. Bunları nasıl birleştireceğini ve birlikte kullanacağını öğrenerek her türlü durumun üstesinden gelecek şekilde kod yazabilirsin. Bununla ilgili olarak Verse Sürekliliğiyle Pist Yarışı Şablonu’ndaki bir örneği ele alalım. Bu örnekte, yarıştan önce her oyuncu için bir tanıtım videosu oynatılmasının yanı sıra oyuncunun tanıtım videosu sırasında oyundan çıkması durumunda tanıtım videosunun iptal edilmesini de sağlayan birden fazla eşzamanlılık ifadesi birleştirilmiştir. Örnek, eşzamanlılığı nasıl birden fazla şekilde kullanabileceğini ve nasıl farklı olaylara dinamik olarak tepki veren dayanıklı kodlar oluşturabileceğini açıkça göstermektedir.
# Wait for the player's intro start and display their info.
# Cancel the wait if they leave.
WaitForPlayerIntro(Player:agent, StartOrder:int)<suspends>:void=
var IntroCounter:int = 0
race:
# Waiting for this player to finish the race and then record the finish.
loop:
sync: