Yordam Üretimi, Oyun Güncellemeleri ve Kelebek Etkisi


10

NOT: Birkaç gün önce Stack Overflow sordum ama çok az görüş vardı ve hiçbir yanıt yoktu. Bunun yerine gamdev.stackexchange üzerinde sormalıydım.

Bu, önceden oluşturulmuş içeriği bozmadan, çoklu yayın sonrası güncellemeleri yoluyla bir prosedür oluşturma sisteminin bakımı hakkında genel bir soru / istektir.

Oyunlar için prosedürel içerik oluştururken "Kelebek Etkisi" sorunlarından kaçınmak için bilgi ve teknikler bulmaya çalışıyorum. Tohumlanmış bir rasgele sayı üreteci kullanırken, tekrarlanabilir bir dünya yaratmak için rasgele sayıların tekrar eden bir sırası kullanılabilir. Bazı oyunlar, üretilen dünyayı bir kez oluşturulduktan sonra diske kaydederken, prosedürel üretimin güçlü özelliklerinden biri, bir bölgeyi aynı şekilde birden çok kez yeniden oluşturmak için sayı dizisinin tekrarlanabilirliğine güvenebilmenizdir. sebat. Özel durumumun kısıtlamaları nedeniyle, kalıcılığı en aza indirmeliyim ve mümkün olduğunca tamamen tohumlanmış konsantrasyona güvenmem gerekiyor.

Bu yaklaşımdaki temel tehlike, prosedürel üretim sistemindeki en ufak bir değişikliğin bile tüm dünyayı değiştiren bir kelebek etkisine neden olabilmesidir. Bu, oyuncuların keşfettiği dünyaları yok etmeden oyunu güncellemeyi çok zorlaştırır.

Bu problemden kaçınmak için kullandığım ana teknik, her biri kendi tohumlanmış rastgele sayı üretecine sahip olan birden fazla fazda prosedürel nesil tasarlamaktır. Bu, her alt sistemin kendi içinde bulunduğu anlamına gelir ve bir şey kırılırsa dünyadaki her şeyi etkilemez. Ancak bu, oyunun yalıtılmış bir parçası olsa bile, hala "kırılma" için çok fazla potansiyele sahip gibi görünüyor.

Bu sorunla başa çıkmanın bir diğer olası yolu, jeneratörlerinizin tam sürümlerini kod içinde tutmak ve belirli bir dünya örneği için doğru jeneratörü kullanmaya devam etmek olabilir. Bu benim için bir bakım kabusu gibi görünüyor ve bunu gerçekten yapan olup olmadığını merak ediyorum.

Bu yüzden sorum, özellikle yayın sonrası oyun güncellemeleri bağlamında, kelebek etkisi sorunuyla başa çıkmak için genel tavsiye, teknikler ve tasarım desenleri için bir talep. (Umarım bu çok geniş bir soru değildir.)

Her ne kadar bu bir dil agnostik soru olmasına rağmen, Unity3D / C # çalışıyorum.

GÜNCELLEME:

Cevaplar için teşekkürler.

Statik verilerin en iyi ve en güvenli yaklaşım olduğu ve giderek daha fazla statik veri depolamanın bir seçenek olmadığı gibi, üretilen bir dünyada uzun bir kampanyaya sahip olmanın, kullanılan jeneratörlerin sıkı bir versiyonunu gerektireceği gibi görünüyor. Benim durumumdaki sınırlamanın nedeni, mobil tabanlı bir bulut kaydetme / senkronizasyon gereksinimidir. Benim çözümüm, önemli şeyler hakkında az miktarda kompakt veri depolamanın yollarını bulmak olabilir.

Stormwind'in "Kafesler" kavramını işler hakkında düşünmenin özellikle faydalı bir yolu olarak görüyorum. Bir kafes temel olarak küçük değişikliklerin yan etkilerini, yani kelebeğin kafasını önleyen bir yeniden besleme noktasıdır.


Bu soruyu konu dışı olarak kapatmak için oy kullanıyorum çünkü çok geniş.
Almo

Çok geniş olduğunun farkındayım. Bunun yerine bir gamedev forumu falan denememi tavsiye eder misiniz? Soruyu daha spesifik hale getirmenin gerçekten bir yolu yok. Bu alanda çok deneyime sahip birinden, bana gelmemiş bazı kurnaz numaralarla duyabileceğimi umuyordum.
null

2
Almo yanılıyor. Hiç de geniş değil. Bu mükemmel bir soru ve iyi cevaplar verilebilecek kadar dar. Birçoğumuzun prosedürist milletinin sık sık düşündüğü bir şey.
Mühendis

Yanıtlar:


8

Sanırım burada üsleri ele aldınız:

  • Yayılmayı değişikliklerden sınırlamak için birden fazla jeneratör kullanmak veya aralıklarla yeniden tohumlama (örn. Mekansal karma kullanarak). Bu muhtemelen kozmetik içeriği için işe yarar, ancak işaret ettiğiniz gibi, yine de bir bölümde bulunan kırılmaya neden olabilir.

  • Kaydetme dosyasında kullanılan jeneratör sürümünü takip etmek ve uygun şekilde yanıt vermek. "Uygun" ne anlama gelebilir ...

    • Oyununuzdaki jeneratörlerin önceki tüm sürümlerinin bir geçmişini çalıştırılabilir durumda tutma ve kaydetme ile eşleşenleri kullanma. Bu, oyuncular eski tasarrufları kullanmaya devam ederse hataları düzeltmeyi zorlaştırır.
    • Oynatıcıyı bu kayıt dosyasının eski bir sürümden olduğu konusunda uyarır ve bu sürüme ayrı bir yürütülebilir dosya olarak erişmek için bir bağlantı sağlar. Birkaç saatten güne süren kampanyalara sahip oyunlar için iyi, haftalarca veya daha uzun süre oynamayı beklediğiniz bir kampanya için kötü.
    • nYürütülebilir dosyalarınızda yalnızca son jeneratör sürümlerini saklayın . Kaydetme dosyası bu son sürümlerden birini kullanıyorsa, kaydetme dosyasını en son sürüme güncelleyin (öner). Bu, eski durumları değişmez değerlere açmak için uygun jeneratörü kullanır (veya çok benziyorsa, aynı jeneratörün çıkışındaki deltalara). Buradan sonra herhangi bir yeni devlet en yeni jeneratörlerden geliyor. Uzun süre oynamamış oyuncular geride kalabilir. Ve en kötü durumda, tüm oyun durumunu tam anlamıyla saklarsınız, bu durumda da ...
  • Nesil mantığınızı sık sık değiştirmeyi bekliyorsanız ve önceki sürümlerle uyumluluğu kırmak istemiyorsanız, jeneratör determinizmine güvenmeyin: tüm durumunuzu kaydetme dosyanıza kaydedin. (yani. "Yörüngeden çek. Emin olmanın tek yolu bu")


Nesil kurallarını oluşturduysanız, nesli tersine çevirmenin bir yolu var mı? Bir oyun durumu verildiğinde IE, bunu bir tohuma geri döndürebilir misiniz? Verilerinizle bu mümkün ise, oynatıcıyı farklı bir oyun sürümüne bağlamak yerine, eski sistemle bir tohumdan bir dünya üreten, daha sonra yeni jeneratör için bir tohum üretmek için oluşturulan durumu kullanan bir güncelleme yardımcı programı sağlayabilirsiniz. Yine de oyuncularınızı dönüşüm için beklemeleri konusunda uyarmanız gerekebilir.
Joe

1
Bu genel olarak mümkün değil. Yeni jeneratör için eskisiyle aynı çıktıyı veren bir tohum olduğu bile garanti edilmiyor. Genellikle bu tohumlar yaklaşık 64 bit içerir, ancak oyununuzun destekleyebileceği olası dünyaların sayısının 2 ^ 64'ten fazla olması muhtemeldir, bu nedenle her jeneratör bunlardan sadece bir alt grup üretir. Jeneratörün değiştirilmesi büyük olasılıkla önceki jeneratörün setiyle çok az hatta hiç kesişmesi olmayan yeni bir seviye alt kümesiyle sonuçlanacaktır.
DMGregory

"Doğru" cevabı seçmek zordu. Bunu seçtim çünkü özlü ve ana konuları net bir şekilde özetledi. Teşekkürler.
null

4

Tek bir numara jeneratörden deterministik tutmak kolay yeterli olacaktır - - ziyade böyle Kelebek Etkisi birincil kaynağı tartışmalı sayı oluşturma değil kullanımı istemci kod tarafından bu sayıların. Kod değişiklikleri, şeyleri sabit tutmanın asıl zorluğudur.

Kod: Birim Testleri Bir yerde küçük bir değişikliğin istemeden başka bir yerde görünmemesini sağlamanın en iyi yolu, yapınıza her üretken yönü için kapsamlı birim testleri eklemektir. Bu, bir şeyi değiştirmenin diğerlerini etkileyebileceği herhangi bir kompakt kod parçası için geçerlidir - herkes için testlere ihtiyacınız vardır, böylece etkilenen tek bir yapıda görebilirsiniz.

Sayılar: Periyodik Diziler / Yuvalar Her şeye hizmet eden bir sayı üreteciniz olduğunu varsayalım. Anlam atamaz, sadece sayıları sırayla verir - herhangi bir PRNG gibi. İki tohumda aynı tohum göz önüne alındığında, aynı dizileri alıyoruz, değil mi? Şimdi bir şeyler düşünüyor ve oyununuzun düzenli olarak rastgele bir değerle sağlanması gereken 30 yönünün olacağına karar veriyorsunuz. Burada 30 slotluk bir bisiklet dizisi atarız, örn. Dizideki her ilk sayı engebeli arazi düzenidir, her ikinci sayı arazi bozulmalarıdır ... vb. ... her 10'uncu sayı gerçekçilik için AI durumuna bir hata ekler. Yani adetiniz 30.

10'dan sonra, oyun tasarımı ilerledikçe diğer yönler için kullanabileceğiniz ücretsiz 20 slotunuz var. Buradaki maliyet, elbette, kullanımda olmasalar da , yani 1-10 arasındaki bir sonraki diziye geri dönmek için, periyodu tamamlasalar bile, 11-30 yuvaları için rakamlar oluşturmanız gerekir . Bir CPU maliyeti vardır, ancak küçük olmalıdır (boş yuva sayısına bağlı olarak). Diğer dezavantajı, nihai tasarımınızın geliştirme sürecinizin en başında hazırladığınız yuva sayısına sığabileceğinden emin olmanız gerekir ... ve başlangıçta ne kadar çok ataırsanız, o kadar "boş" yuvalar her şeyin yolunda gitmesi, her şeyin yolunda gitmesi gerekir.

Bunun etkileri:

  • Her şey için sayı üreten bir jeneratörünüz var
  • Sayı oluşturmak için ihtiyaç duyduğunuz yönlerin sayısını değiştirmek determinizmi etkilemez (süreniz tüm yönleri karşılayacak kadar büyük olması şartıyla)

Tabii ki, oyunun kamuya açık olmadığı uzun bir süre olacak - alfa olarak, tabiri caizse - böylece herhangi bir oyuncuyu etkilemeden 30'dan 20'ye düşebilirsin, atanmış olan yolu başlangıcında çok fazla yuva. Bu elbette bazı CPU döngülerini kurtaracaktır. Ancak, iyi bir karma işlevinin (kendiniz yazabilirsiniz) her şeye rağmen yıldırım hızında olması gerektiğini unutmayın. Bu nedenle, ekstra slot çalıştırmak zorunda kalmanın maliyeti yüksek olmamalıdır.


Selam. Bu yaptığım bazı şeylere benziyor. Genellikle ilk dünya tohumuna dayanarak bir avuç alt tohum üretiyorum. Son zamanlarda uzunca bir gürültü dizisi üretmeye başladım ve sonra her "slot" o dizinin içine bir indeks. Bu şekilde her alt sistem doğru tohumu alabilir ve tek başına çalışabilir. Bir başka harika teknik, her bir konum için bir tohum oluşturmak için x, y koordinatlarını kullanmaktır. Euphoric'in bu yığın sayfasındaki cevabının kodunu kullanıyorum: programmers.stackexchange.com/questions/161336/…
null

3

PCG ile kalıcılık istiyorsanız , PCG kodunun kendisine veri olarak davranmanızı öneririm . Nasıl düzenli içerikli, oluşturulmuş içerikli revizyonlarda veri tutacağınız gibi, revizyonlar arasında da devam etmek istiyorsanız jeneratörü devam ettirmeniz gerekir.

Tabii ki, en popüler yaklaşım, bahsettiğiniz gibi, üretilen verileri statik verilere dönüştürmektir.

Birçok jeneratör versiyonunu koruyan oyunların örneklerini bilmiyorum, çünkü PCG oyunlarında kalıcılık olağandışı - bu yüzden permadeath genellikle PCG ile el ele gidiyor. Ancak, aynı oyunda bile aynı tipte birden fazla PCG örneği vardır. Örneğin, Unangband'ın zindan odaları için birçok ayrı jeneratörü vardır ve yenileri eklendikçe, eskileri hala aynı şekilde çalışır. Bunun sürdürülebilir olup olmadığı uygulamanıza bağlıdır. Onu sürdürülebilir kılmanın bir yolu, jeneratörlerinizi uygulamak için komut dosyalarını kullanmak ve oyun kodunun geri kalanıyla izole tutmaktır.


Farklı alanlar için farklı jeneratörleri kullanmak akıllıca bir fikirdir.
null

2

Çeşitli şeylerin rastgele yerleştirilmesinin yanı sıra yaklaşık 1 milyon bina ve diğer nesneleri tutan yaklaşık 30000 kilometrekarelik bir alanı koruyorum. Dış mekan simülasyonu ofc. Saklanan veriler yaklaşık 4 GB'dir. Depolama alanım olduğu için şanslıyım, ancak sınırsız değil.

Rastgele rastgele, kontrolsüz. Ama biraz kafese sokabiliriz:

  • Başlangıç ​​bitiş ucunu kontrol edin (diğer gönderilerde, tohum numarası ve kaç tane üretilen numarada belirtildiği gibi).
  • Sayısal alanını sınırlayın, örn. yalnızca 0 ile 100 arasında tamsayılar üretir.
  • Bir değer ekleyerek sayısal alanını kaydırın (örn. 100 + [0 ile 100 arasında oluşturulan sayılar] 100 ile 200 arasında rastgele sayılar üretir)
  • Ölçeklendirin (örneğin, 0.1 ile çarpın)
  • Ve etrafına çeşitli kafesler uygulayın. Bu, neslin bir kısmını azaltarak hurdaya ayırıyor. Örneğin. 2 boyutlu uzayda üretim yapılıyorsa, sayı çiftlerinin üstüne bir dikdörtgen konabilir ve dışarıdakileri kazınabilir. Veya bir daire veya bir çokgen. 3D uzayda ise, örneğin sadece bir kürenin içinde bulunan üçüzleri veya başka bir şekli kabul edebilir (şimdi görsel olarak düşünüyor, ancak bunun gerçek görselleştirme veya konumlandırma ile ilgisi yoktur).

Bu kadar. Kafesler de maalesef veri tüketiyor.

Fince bir söz var, Hajota ja hallitse. İçine çevirir Böl ve Yönet .

En küçük ayrıntıların kesin olarak tanımlanması fikrini hızla terk ettim. Rastgele özgürlük istiyor, bu yüzden özgürlüğü elde etti. Kelebek uçsun - kafesin içinde. Bunun yerine kafesleri tanımlamak için zengin bir yola sahip olmaya odaklandım. Mavi veya koyu mavi oldukları sürece hangi arabaların oldukları önemli değil (sıkıcı bir işveren bir keresinde söyledi :-)). "Mavi veya koyu mavi" burada renk boyutu boyunca (çok küçük) kafestir.

Sayısal alanları kontrol etmek ve yönetmek için yönetilebilir nedir?

  • Boole ızgarası (bitler küçük!)
  • Köşe noktaları
  • Bir ağaç yapısı gibi (= "kafesleri tutan kafeslere" takip etmek)

Bakım ve sürüm uyumluluk açısından ... elimizde
: eğer sürüm = n sonra
: elseif sürüm = m sonra ...
Evet, kod tabanı büyür :-).

Tanıdık şeyler. İlerlemek için doğru yol , bölmek ve fethetmek için zengin bir yöntem tanımlamak olacaktır. ve bununla ilgili bazı verileri feda . Daha sonra, mümkünse, (yerel) randomizasyon özgürlüğünü verin, burada kontrol etmek çok önemli değildir.

DMGregory tarafından önerilen komik "nuke it fom yörünge" ile tamamen uyumsuz değil, ama belki de küçük ve doğru nükleer kullanın? :-)


Cevabınız için teşekkürler. Bu, bakımı büyük ölçüde prosedürel bir alan gibi geliyor. Bu kadar geniş bir alan için nasıl olduğunu görebiliyorum, çok fazla depolama alanına erişiminiz olsa bile, her şeyi saklamak hala imkansız. Sürüm oluşturucuların ileriye doğru yol alması gerektiği anlaşılıyor. Öyle olsun :)
null

Tüm cevaplardan kendimi en çok bu konuda düşünürken buluyorum. Şeylerin hafif felsefi tanımlarından keyif aldım. Fikirleri açıklarken "Kafes" terimini çok faydalı buluyorum, bu yüzden bunun için teşekkürler. Kelebeğin kafesinde uçmasına izin ver :)
null

PS Hangi oyun üzerinde çalıştığınızı gerçekten çok merak ediyorum. Bu bilgileri paylaşabilir misiniz?
null

Sayısal alan hakkında bir şey daha ekleyebiliriz: Her zaman sıfıra yakın kalmaya değer. Muhtemelen zaten bildiğini. Sıfıra yakın en iyi sayısal doğruluğu verir ve çoğu en az bit için vurur. Daha sonra her zaman sıfıra yakın sayıların bir kısmını telafi edebilirsiniz, ancak bunun için yalnızca tek bir sayıya ihtiyacınız vardır. Benzer şekilde, uzak bir hesaplamayı bir ofset ile sıfıra yakın (neredeyse ZORUNLU söyleyebilirim) diyebilirsiniz. - Stormwind
Stormwind

Öncekini değerlendirirken, küçük bir araç hareketini, 0,01 [metre, birim] 1'lik bir çerçeve artışını düşünün: 32 bit sayısal doğrulukta (tek) 10000.1 + 0.01'i doğru bir şekilde hesaplayamazsınız, ancak 0.1 + 0.01'i hesaplayabilirsiniz. Bu nedenle, "eylem" çok uzakta gerçekleşirse (dağların arkasında :-)), oraya gitmeyin, dağları kendinize taşıyın (10000 ile hareket edin, o zaman şimdi 0.1'desınız). Depolama alanı için de geçerlidir. Sayısal değerlerin birbirine yakın depolanmasıyla açgözlü olabilir. Bunların ortak kısmını bir kez saklayın ve varyasyonları ayrı ayrı saklayın - bitleri kurtarabilir! Bağlantıyı buldunuz mu? ;-)
Stormwind
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.