Geliştirici Günlüğü #5: Oyun kuralları nasıl yazılır?

Kısaca: MKA Engine'de oyunun içeriği (ülkeler, teknolojiler, vergi kuralları, olaylar) .bsm uzantılı metin dosyalarında yazılır. Betik dilinin üç biçimi var: koşul bir şeyi sınar, etki durumu değiştirir, formül bir sayı üretir. Dil bilerek sınırlıdır: kayan nokta yoktur, formüller döngüsüz ve atamasızdır, her hata dosya, satır ve sütunla bildirilir ve hiçbir girdi motoru çökertmez.
Bir masa oyununu düşünün. Kural kitapçığı oyunun ne olduğunu söyler: "bir ülkenin altını 100'ün üstündeyse şunu yapabilir", "vergi gelirin beşte biridir". Kitapçığı yazan kişi masayı kuran hakemin nasıl çalıştığını bilmek zorunda değildir; ama kitapçığın her cümlesi belirsizliğe yer bırakmayacak kadar net olmalıdır. MKA Engine'de hakem motordur, kural kitapçığı ise oyunun içerik dosyalarıdır. Bu günlük o kitapçığın dilini anlatıyor.
İçerik neden metin?
Önceki günlüklerde motorun belirlenimci çekirdeğini gördük. Oyun ise çekirdeğin üstünde bir moddur: tabloları, kuralları ve sayıları oyun tanımlar, motor yalnız düzeneği verir. Oyunun içeriğini Rust koduna gömmek, her sayı değişikliğinde yeniden derleme demek olurdu ve moddaki oyuncunun elinden tek aracı alırdı. Bu yüzden içerik düz metindir: bir editörle açılır, sürüm denetimine girer, farkı okunur.
Motorun okuduğu bütün metin dosyaları .bsm uzantısını taşır; oyunun kökünde project.bsm adlı bir manifest, hangi içerik türünün hangi klasörde durduğunu ve alanlarının neler olduğunu söyler. Örneğin mal tanımları common/goods/ altındaki dosyalardadır; bir dosya hangi tür klasörünün altındaysa o türe aittir.
Dosya biçimi
Bir dosya, anahtar = değer girdilerinden oluşur. Değer bir sözcük, tırnaklı metin, sayı, tarih (YYYY.MM.DD) ya da süslü parantezli bir blok olabilir. # satırın kalanını yorum yapar. @ad = değer yalnız o dosyada geçerli bir tanım yazar, $ARG$ ise bir makro parametresidir.
Ondalıklı sayılar kayan nokta değil, Günlük #1'de anlattığımız sabit noktalı sayı olarak okunur; içerik dilinde hiçbir yerde float yoktur.
Dosyalar belirli bir sırayla yüklenir: önce mod sırası, sonra dosya yolunun bayt sırası, sonra dosyadaki yazılış sırası. Bu sıra sabit olduğu için kimlikler her bilgisayarda aynı atanır. Bu da çok oyunculu oyunun şartıdır (Günlük #2).
Üç biçim: koşul, etki, formül
Dilin işi üç soruya ayrılır. "Bu doğru mu?", "Ne değişsin?", "Kaç?" Her birinin biçimi ayrıdır ve karıştırılamaz; böylece bir koşul yanlışlıkla bir şeyi değiştiremez.
Örnekler motorun kılavuzundaki örnek dünyadan; gerçek bir oyunun kuralı değildir.
Koşul bir kapsamın durumunu sınar. Kapsam bir tablo satırıdır (bir ülke, bir eyalet). Örneğin "zengin" koşulu "altın 100'den çok mu?" diye sorar; "kalabalık" koşulu "ülkenin sahip olduğu eyaletlerden birinin gelişmişliği 2'nin üstünde mi?" diye sorar.
Girdiler VE ile birleşir; OR, NOT gibi mantık blokları vardır. Bir bağlantı boşsa koşul yanlış sayılır, çökme yaratmaz.
Etki durumu değiştirir. Yazıldığı sırayla çalışır ve her etki öncekinin bıraktığı durumu görür. Örneğin "vergi topla" etkisi şöyle işler: altın 100'ün altındaysa 5 altın ekle, değilse 1 altın ekle.
Formül bir sayı üretir. İşlemler yazıldığı sırayla tek bir biriktiricide çalışır. Örneğin "vergi tabanı" altının beşte biridir ve sıfırın altına inmez; "gelir" 100'e vergi tabanını ekler.
| Biçim | Sorusu | Durumu değiştirir mi? | Rastgele sayı çekebilir mi? |
|---|---|---|---|
| Koşul | Bu doğru mu? | Hayır | Hayır |
| Formül | Kaç? | Hayır | Hayır |
| Etki | Ne değişsin? | Evet | Yalnız olay, komut ve kanca anında |
Neden formül dili döngüsüz?
Formül dili bilerek döngüsüz ve atamasızdır. Sebebi iki yönlü. Birincisi, herkesin bilgisayarı aynı hesabı aynı sırayla yapmalı; döngüsüz bir ifadenin bittiği baştan bellidir. İkincisi, oyun bir mod olduğu için içeriği başkaları da yazacak; bir formülün yanlışlıkla donmaması, modu yazanın hatası yüzünden bütün oyunun takılmaması demektir.
Etkilerde dolaşma var: any_, every_, random_ ve ordered_ ön ekleriyle bir koleksiyonun üyeleri gezilir. Ama bu dolaşmalar da sınırlıdır: tek bir değerlendirmede en çok 1.000.000 üye dolaşılır, while en çok 1.000 tur döner. Sınır aşılırsa betik durur ve hata kaydeder; oyun çökmez. Bu sınırlar oyunun ayarıdır ve çok oyunculu oyunda sonucu etkilediği için bütün oyuncularda aynı olması gerekir; bu yüzden mod seti özetine girer.
Bir mod başka bir modu nasıl yamar?
Bir sonraki mod, önceki modun kaydını üç biçimde değiştirebilir:
anahtar = { ... }kaydın tamamını yerine koyar,anahtar.alan = değeryalnız tek alanı değiştirir,anahtar = deletekaydı siler.
İki mod aynı kaydı değiştirirse çakışma sessizce geçilmez, raporlanır. Bu, "benim modum seninkinin üstüne yazdı" sürprizini önler.
İçerik ne zaman çalışır?
Bu, bir oyun gününün sırasıyla ilgili: olaylar ve her gün çalışan eylemler, günün içerik evresinde, komutlardan ve zamanlayıcılardan sonra, sistemlerden önce işler. Küçük ama önemli sonucu şu: bir koşul, sistemlerin ve kancaların bir önceki adımda yazdığını görür, aynı adımdakini asla. Bir sistemin bugün koyduğu işaret yarın işler. İçeriğin yazdığını ise aynı günün sistemleri görür.
Rastgelelik de aynı disipline bağlı: çekiliş yalnız random ve random_list ile, yalnız olay, komut ve kanca anında yapılır ve Günlük #1'deki durumsuz üreteçten gelir. Betik bunun dışında rastgele sayı üretemez.
Üç bağlam: kim neyi okur?
Aynı betik üç ayrı bağlamda değerlendirilebilir:
- Simülasyon: yalnız her makinede aynı olan veriyi okur; sonucu herkeste aynıdır.
- Yerel (arayüz): arayüz durumunu okuyabilir ama hiçbir şey yazamaz ve rastgele sayı çekemez.
- Varsayımsal: "şunu yapsam ne olurdu" hesabıdır ve durumu bozmaz.
Makineye özgü bir bilgi simülasyona yalnız komutla girer; komut ağdan herkese gittiği için herkeste aynı olur.
Çok dilli metin
Oyunun görünen metinleri içerik dosyalarında değil, dil başına bir yerelleştirme dosyasındadır: localization/<dil>.bsm, içinde <dil> = { anahtar = "metin" }. İlk dil başvuru dilidir; başvuru dilde olmayan bir anahtar tanı verir, böylece bir metin unutulmuş olarak gönderilmez.
Yazdığımız kuralı nasıl yoklarız?
İçerik yazılırken iki araçla çalışılır. bsm check dosyaları okur: bilinmeyen bir alan, yanlış türde bir değer, eksik bir başvuru dosya:satır:sütun ile bildirilir. Pahalı kalıplar için uyarı da verir; örneğin sık çalışan bir işte her satırı gezmek ya da bir gezinmenin içinde bir daha gezinmek. bsm test her yeni kural için yazdığımız küçük içerik sınamalarını koşar: bir başlangıç durumu, birkaç adım ve beklenen sonuç. Örneğin "bir ülke bu kuralı tetiklerse altını şu kadar artsın". Kural yazılırken sınamasının da yazılması bizim alışkanlığımız.
Sınırlar
- Dil bilerek dar: Formüllerde döngü ve atama yok. Karmaşık bir hesap gerekiyorsa bu, Rust ile yazılan WebAssembly moduna ya da motor ilkellerine kalır.
- Sözcük listesi örnek dünyaya göre: Anahtar sözcüklerin listesi motorun örnek dünyasından üretilir. Kendi oyununuzun kapsamları ve içerik türleri
project.bsmiçinde tanımlanır; ilk sürümde bu rehber o örnek dünyayı esas alır. - Sınırlar bir emniyet ağıdır: 1.000.000 üye ve 1.000 tur gibi sınırlar donmayı önler, ama pahalı bir kuralı hızlı yapmaz. Yavaş kalıpları
bsm checkuyarısı ve ölçüm yakalar. - Ölçüm yok: Bu günlükte dilin hızına dair bir ölçüm vermiyoruz; anlattığımız şey dilin biçimi ve kuralları.
- Motor geliştirme aşamasında: Dilin ayrıntıları ve anahtar sözcükleri değişebilir.
Sık sorulanlar
Strateji oyunu kuralları kodla mı yazılır?
MKA Engine'de oyunun içeriği ve kuralları, .bsm uzantılı metin dosyalarında bir betik diliyle yazılır; motor çekirdeği Rust'ta kalır. Yeni bir sistem gerekiyorsa o, Rust ile yazılan bir WebAssembly moduyla eklenir.
Mod yazmak için programlama bilmek gerekir mi?
İçerik için gerekmez: bir mal, bir olay ya da bir vergi kuralı metin dosyasında anahtar-değer olarak yazılır. Motorda yeni bir sistem eklemek isteyen modun Rust bilmesi gerekir.
Betik dilinde döngü var mı?
Formüllerde yok. Etkilerde bir koleksiyonu gezen every_ gibi ön ekler ve while var, ama ikisinin de üst sınırı var ve aşıldığında oyun çökmez, betik hata kaydederek durur.
Bir betik hatası oyunu çökertir mi?
Hayır. Her hata dosya, satır ve sütunla bildirilir ve hiçbir girdi motoru çökertmez. Bir bağlantı boşsa koşul yanlış sayılır; sınır aşılırsa betik durur.
Kaynaklar ve kayıtlar
Dış kaynaklar:
- Vikipedi, Domain-specific language (alana özgü dil) (yeni sekmede açılır)
- Vikipedi, Video game modding (oyun modlama) (yeni sekmede açılır)
İç kayıtlarımız (motorun deposu kapalı olduğu için bağlantı veremiyoruz):
- Oyun da bir moddur: mantık Rust'ta WebAssembly olarak, içerik veri dosyaları ve döngüsüz formül diliyle yazılır.
- İçerik dili kararı: sıralı etki dili, döngüsüz ve atamasız formül dili; etkiler yalnız olay, komut ve kanca anında koşar.
- Betik bağlamları ve sınırları: üç değerlendirme bağlamı, donmayı önleyen sınırlar ve oyunun ayarı olarak yönetimi.
- Metin biçimi ve çok dillilik:
.bsmuzantısı, manifestproject.bsm, dil başına yerelleştirme dosyası.
Bu konuda daha fazlası gelecek
MKA Engine hâlâ geliştirme aşamasında. Betik diliyle ilgili paylaşacaklarımız bu günlükle bitmiyor: olaylar ve çarpanlar, yapay zekânın içerikle yazılması, savaşın ve ekonominin içerik tarafı ve modlama araçları ayrı günlüklerde kendi ayrıntısıyla gelecek. Dil geliştikçe bu günlükteki bilgileri güncellemek yerine yeni günlüklerle ekleyeceğiz.
Bu günlük hakkında sorularınız ya da önerileriniz varsa bize iletişim sayfasından yazabilirsiniz. Motorun genel tanıtımı için MKA Engine sayfasına, bütün günlükler için Geliştirici Günlükleri listesine bakabilirsiniz.