Aynı bileşen kümesinin varlıklarını doğrusal belleğe gruplama


12

Temel sistem-bileşen-varlık yaklaşımından başlıyoruz .

Sadece bileşen türleri hakkında bilgi dışında derlemeler ( bu makaleden türetilen terim) oluşturalım . Çalışma zamanında dinamik olarak yapılır, tıpkı bir varlığa bileşenleri tek tek ekleyeceğimiz / kaldıracağımız gibi, ancak yalnızca tür bilgileriyle ilgili olduğundan daha kesin bir şekilde adlandıralım.

Sonra her biri için montaj belirleyen varlıklar inşa ediyoruz . Varlığı oluşturduktan sonra, montajı değişmezdir, bu da onu yerinde doğrudan değiştiremeyeceğimiz anlamına gelir, ancak yine de mevcut varlığın yerel bir kopyaya imzasını alabilir (içeriklerle birlikte), üzerinde uygun değişiklikler yapabilir ve yeni bir varlık oluşturabiliriz. onun.

Şimdi anahtar kavram için: bir varlık yaratıldığında, montaj kovası adı verilen bir nesneye atanır , bu da aynı imzanın tüm varlıklarının aynı kapta (örn. Std :: vector'da) olacağı anlamına gelir .

Şimdi sistemler sadece ilgilendikleri her kovadan tekrar ediyorlar ve işlerini yapıyorlar.

Bu yaklaşımın bazı avantajları vardır:

  • bileşenler birkaç (tam olarak: kova sayısı) bitişik bellek yığınında saklanır - bu bellek kolaylığını artırır ve tüm oyun durumunu boşaltmak daha kolaydır
  • sistemler bileşenleri doğrusal bir şekilde işler, bu da gelişmiş önbellek tutarlılığı - güle güle sözlükleri ve rastgele bellek sıçramaları anlamına gelir
  • yeni bir varlık oluşturmak, bir montajı kovaya eşlemek ve gerekli bileşenleri vektörüne geri itmek kadar kolaydır
  • bir varlığın silinmesi std :: 'ye yapılan bir çağrı kadar kolaydır, çünkü son öğe silinmiş olanla değiştirilir.

resim açıklamasını buraya girin

Tamamen farklı imzalara sahip çok sayıda varlığımız varsa, önbellek tutarlılığının faydaları biraz azalır, ancak çoğu uygulamada olacağını düşünüyorum.

Vektörler yeniden tahsis edildikten sonra işaretçi geçersiz kılma ile ilgili bir sorun da vardır - bu, aşağıdaki gibi bir yapı eklenerek çözülebilir:

struct assemblage_bucket {
    struct entity_watcher {
        assemblage_bucket* owner;
        entity_id real_index_in_vector;
    };

    std::unordered_map<entity_id, std::vector<entity_watcher*>> subscribers;

    //...
};

Bu nedenle, oyun mantığımızda herhangi bir nedenle yeni oluşturulan bir varlığı takip etmek istediğimizde, kova içinde bir entity_watcher kaydederiz ve varlık kaldırılırken std :: move'd olması gerektiğinde, izleyicilerini arar ve güncelleriz onların real_index_in_vectoryeni değerlere. Çoğu zaman bu, her varlık silme işlemi için yalnızca tek bir sözlük araması uygular.

Bu yaklaşımın başka dezavantajları var mı?

Oldukça açık olmasına rağmen, çözüm neden hiçbir yerde belirtilmiyor?

EDIT : Yorumlar yetersiz olduğu için "cevapları cevaplamak" sorusunu düzenliyoruz.

statik sınıf yapısından uzaklaşmak için özel olarak yaratılan takılabilir bileşenlerin dinamik doğasını kaybedersiniz.

Yapmıyorum. Belki yeterince açık bir şekilde açıklamadım:

auto signature = world.get_signature(entity_id); // this would just return entity_id.bucket_owner->bucket_signature or so
signature.add(foo_component);
signature.remove(bar_component);
world.delete_entity(entity_id); // entity_id would hold information about its bucket owner
world.create_entity(signature); // automatically assigns new entity to an existing or a new bucket

Mevcut varlığın imzasını almak, değiştirmek ve yeni bir varlık olarak tekrar yüklemek kadar basittir. Takılabilir, dinamik doğa ? Elbette. Burada sadece bir "montaj" ve bir "kova" sınıfı olduğunu vurgulamak istiyorum . Kovalar veriye dayalıdır ve çalışma zamanında optimum miktarda oluşturulur.

geçerli bir hedef içerebilecek tüm bölümlerden geçmeniz gerekir. Harici bir veri yapısı olmadan, çarpışma tespiti de aynı derecede zor olabilir.

Bu yüzden yukarıda belirtilen dış veri yapılarına sahibiz . Çözüm, System sınıfında bir sonraki gruba ne zaman atlanacağını algılayan bir yineleyici eklemek kadar basittir. Atlama mantığına tamamen şeffaf olacaktır.


Ayrıca, tüm bileşenleri vektörlerde saklamak ve sistemlerinin sadece bunları işlemesine izin vermek için Randy Gaul makalesini okudum. Orada iki büyük sorun görüyorum: Ya sadece bir varlık alt kümesini güncellemek istersem (örneğin itlaf etmeyi düşünün). Bu bileşenler nedeniyle tekrar varlıklarla birleştirilecektir. Her bileşen yineleme adımı için ait olduğu varlığın bir güncelleme için seçilip seçilmediğini kontrol etmem gerekir. Diğer sorun, bazı sistemlerin önbellek tutarlılık avantajını tekrar ortadan kaldırarak birden fazla farklı bileşen türünü işlemesi gerektiğidir. Bu sorunlarla nasıl başa çıkılacağına dair bir fikrin var mı?
tiguchi

Yanıtlar:


7

Temelde bir havuz ayırıcısı ve dinamik sınıflar içeren statik bir nesne sistemi tasarladınız.

Okul günlerimde neredeyse "meclisler" sisteminizle neredeyse aynı şekilde çalışan bir nesne sistemi yazdım, ancak her zaman kendi tasarımlarımda "meclisler" ya da "taslaklar" ya da "arketipler" deme eğilimindeyim. Mimari, saf nesne sistemlerinden daha popodaki bir acıydı ve kıyasladığım daha esnek tasarımlardan bazılarına kıyasla ölçülebilir performans avantajları yoktu. Bir nesneyi yeniden tanımlamaya veya yeniden tahsis etmeye gerek kalmadan dinamik olarak değiştirme yeteneği, bir oyun düzenleyicisi üzerinde çalışırken çok önemlidir. Tasarımcılar bileşenleri nesne tanımlarınıza sürükleyip bırakmak isteyeceklerdir. Kişisel olarak beğenmediğim halde, çalışma zamanı kodunun bile bazı tasarımlarda bileşenleri verimli bir şekilde değiştirmesi gerekebilir. Düzenleyicinizde nesne referanslarını nasıl bağladığınıza bağlı olarak,

Önemsiz vakaların çoğunda sandığınızdan daha kötü önbellek tutarlılığı elde edeceksiniz. Örneğin, AI sisteminiz Renderbileşenleri umursamıyor, ancak her bir varlığın parçası olarak bunların üzerinde yinelenerek takılıyor. Yinelenen nesneler daha büyüktür ve önbellek istekleri gereksiz verilerle sonuçlanır ve her istekte daha az tam nesne döndürülür). Hala naif yöntemden daha iyi olacak ve naif yöntem nesnesi kompozisyonu büyük AAA motorlarında bile kullanılıyor, bu yüzden muhtemelen daha iyi ihtiyacınız yok , ama en azından daha fazla geliştiremeyeceğinizi düşünmeyin.

Yaklaşımınız bazıları için en mantıklıbileşenleri, ancak hepsi değil. ECS'yi kesinlikle sevmiyorum çünkü her bileşeni ayrı bir kap içine koymayı savunuyor, bu da fizik veya grafikler için mantıklı geliyor ya da birden fazla komut dosyası bileşenine veya oluşturulabilir AI'ya izin verirseniz hiç mantıklı değil. Bileşen sisteminin yalnızca yerleşik nesnelerden daha fazlası için değil, aynı zamanda tasarımcıların ve oyun programcılarının nesne davranışı oluşturmasının bir yolu olarak kullanılmasına izin verirseniz, tüm AI bileşenlerini (genellikle etkileşime girecek) veya tüm komut dosyasını birlikte gruplandırmak mantıklı olabilir. bileşenleri (hepsini tek bir toplu işte güncellemek istediğiniz için). En performanslı sistemi istiyorsanız, bileşen tahsisi ve depolama şemalarının bir karışımına ihtiyacınız olacak ve her bir bileşen türü için en iyi olanı kesin olarak bulmak için zaman ayıracaksınız.


Dedim ki: Varlığın imzasını değiştiremeyiz ve doğrudan yerinde değiştiremeyeceğimizi kastetmiştim, ancak yine de mevcut topluluğu yerel bir kopyaya alabilir, üzerinde değişiklik yapabilir ve yeni bir varlık olarak tekrar yükleyebiliriz - ve bunlar operasyonda oldukça ucuz, soruda da görüldüğü gibi. Bir kez daha - sadece BİR "kova" sınıfı var. "Assemblages" / "İmzalar" / "İstediğimiz gibi adlandıralım" standart bir yaklaşımda olduğu gibi çalışma zamanında dinamik olarak oluşturulabilir, hatta bir varlığı "imza" olarak düşünmek kadar ileri giderdim.
Patryk Czachurski

Ve ben de bu yeniden yapılanma ile uğraşmak istemediğini söyledim. "Yeni bir varlık oluşturmak", tutma sisteminizin nasıl çalıştığına bağlı olarak varlığa ait mevcut tüm tanıtıcıları kırmak anlamına gelebilir. Yeterince ucuz olup olmadıkları takdirde çağrınız. Sadece popo ile uğraşmak zorunda kaldım.
Sean Middleditch

Tamam, şimdi bununla ilgili fikriniz var. Her neyse, ekleme / kaldırma biraz daha pahalı olsa bile, o kadar çok gerçekleşir ki, gerçek zamanlı olarak gerçekleşen bileşenlere erişim sürecini büyük ölçüde basitleştirmeye değer. Yani, "değişme" yükü göz ardı edilebilir. AI örneğiniz hakkında, yine de birden fazla bileşenden verilere ihtiyaç duyan bu birkaç sisteme değmez mi?
Patryk Czachurski

Demek istediğim, AI'nın yaklaşımınızın daha iyi olduğu bir yerdi, ancak diğer bileşenler için mutlaka böyle değil.
Sean Middleditch

4

Yaptığınız şey yeniden tasarlanmış C ++ nesneleri. Bunun açık hissetmesinin nedeni, "varlık" kelimesini "sınıf" ve "bileşen" ile "üye" olarak değiştirirseniz, bu, mixins kullanan standart bir OOP tasarımıdır.

1) statik sınıf yapısından uzaklaşmak için özel olarak yaratılan takılabilir bileşenlerin dinamik doğasını kaybedersiniz.

2) bellek tutarlılığı, tek bir yerde birden çok veri türünü birleştiren bir nesne içinde değil, bir veri türü içinde en önemlisidir. Sınıf + nesne belleği parçalamasından uzaklaşmak için bileşen + sistemlerinin oluşturulmasının nedenlerinden biri budur.

3) bu tasarım aynı zamanda C ++ sınıf stiline geri döner, çünkü bir bileşen + sistem tasarımında varlık yalnızca iç işlerin insanlar tarafından anlaşılabilir olmasını sağlayan bir etiket / kimlik olduğunda varlığı tutarlı bir nesne olarak düşünürsünüz.

4) bir bileşen için kendini programlamak karmaşık bir nesneden çok kendi içinde birden fazla bileşeni serileştirmek kadar kolaydır, eğer bir programcı olarak takip edilmesi daha kolay değilse.

5) bu yolda bir sonraki mantıklı adım, Sistemleri kaldırmak ve bu kodu doğrudan çalışmak için gereken tüm verilere sahip olduğu varlığa koymaktır. Bunun ne anlama geldiğini görebiliriz =)


2) belki de önbelleği tam olarak anlayamıyorum, ama diyelim ki 10 bileşenle çalışan bir Sistem var. Standart bir yaklaşımda, her bir varlığın işlenmesi RAM'in 10 kez erişilmesi anlamına gelir, çünkü bileşenler havuzlar kullanılsa bile bellekte rastgele yerlere saçılır - çünkü farklı bileşenler farklı havuzlara aittir. Tüm varlıkları bir kerede önbelleğe almak ve tüm bileşenleri tek bir önbellek kaçırmadan, sözlük aramaları yapmak zorunda kalmadan işlemek "önemli" olmaz mı? Ayrıca, 1) noktasını kapsayacak şekilde bir düzenleme yaptım
Patryk Czachurski

@Sean Middleditch'in cevabındaki bu önbellek dökümü hakkında iyi bir açıklaması var.
Patrick Hughes

3) Hiçbir şekilde tutarlı nesneler değildir. A bileşeninin bellekteki B bileşeninin hemen yanında olması, John'un işaret ettiği gibi, "mantıksal tutarlılık" değil, sadece "bellek tutarlılığı" dır. Kovalar, yaratıldıklarında, istenen herhangi bir sıraya imza atarak bile bileşenleri karıştırabilir ve ilkeler hala korunacaktır. 4) yeterli soyutlamaya sahipsek "takip etmek" de aynı derecede kolay olabilir - bahsettiğimiz şey sadece yineleyicilerle sağlanan bir depolama şemasıdır ve belki de bir bayt ofset haritası işlemeyi standart bir yaklaşım kadar kolay hale getirebilir.
Patryk Czachurski

5) Ve bu fikirdeki hiçbir şeyin bu yönü gösterdiğini sanmıyorum. Sizinle aynı fikirde olmak istemiyorum, sadece bu tartışmanın nereye gidebileceğini merak ediyorum, yine de muhtemelen bir tür "ölç" ya da iyi bilinen "erken optimizasyon" u yol açacaktır. :)
Patryk Czachurski

@PatrykCzachurski ama sistemleriniz 10 bileşenle çalışmıyor.
user253751

3

Varlıkları bir arada tutmak düşündüğünüz kadar önemli değil, bu yüzden "bir birim" dışında geçerli bir neden düşünmek zor. Ancak bunu gerçekten mantıksal tutarlılığın aksine önbellek tutarlılığı için yaptığınız için, mantıklı olabilir.

Karşılaşabileceğiniz zorluklardan biri, farklı kovalardaki bileşenler arasındaki etkileşimlerdir. Örneğin, AI'nızın vurabileceği bir şey bulmak son derece basit değildir , geçerli bir hedef içerebilecek tüm kovalardan geçmeniz gerekir. Harici bir veri yapısı olmadan, çarpışma tespiti de aynı derecede zor olabilir.

Varlıkları mantıklı tutarlılık için bir araya getirmeye devam etmek için, varlıkları bir arada tutmamın tek nedeni, görevlerimde tanımlama amaçlıdır. Sadece A tipi B veya B tipi varlık oluşturduysanız bilmeniz gerekir ve bu ile almak ... bunu tahmin: Bu varlık bir araya toplayan tanımlayan yeni bir bileşen ekleyerek. O zaman bile, büyük bir görev için tüm bileşenleri bir araya getirmiyorum, sadece ne olduğunu bilmem gerekiyor. Bu yüzden bu kısmın çok yararlı olduğunu düşünmüyorum.


Cevabınızı tam olarak anlamadığımı itiraf etmeliyim. "Mantıksal tutarlılık" ile ne demek istiyorsun? Etkileşimlerdeki zorluklar hakkında bir düzenleme yaptım.
Patryk Czachurski

"Mantıksal tutarlılık", olduğu gibi: Bir Ağaç varlığını oluşturan tüm bileşenleri birbirine yakın tutmak "mantıklıdır".
John McDonald
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.