Bileşen-nesne iletişimini güvenli ve önbellek dostu bileşen depolama ile nasıl destekleyebilirim?


9

Bileşen tabanlı oyun nesnelerini kullanan bir oyun yapıyorum ve her bileşenin oyun nesnesiyle iletişim kurması için bir yol uygulamakta zorlanıyorum. Her şeyi bir kerede açıklamak yerine, ilgili örnek kodun her bir bölümünü açıklayacağım:

class GameObjectManager {
    public:
        //Updates all the game objects
        void update(Time dt);

        //Sends a message to all game objects
        void sendMessage(Message m);

    private:
        //Vector of all the game objects
        std::vector<GameObject> gameObjects;

        //vectors of the different types of components
        std::vector<InputComponent> input;
        std::vector<PhysicsComponent> ai;
        ...
        std::vector<RenderComponent> render;
}

GameObjectManagerTüm oyun nesneleri ve onların bileşenleri tutar. Oyun nesnelerini güncellemekten de sorumludur. Bunu, bileşen vektörlerini belirli bir sırayla güncelleyerek yapar. Aynı anda var olabilecek oyun nesnelerinin sayısında neredeyse hiçbir sınırlama olmaması için diziler yerine vektörler kullanıyorum.

class GameObject {
    public:
        //Sends a message to the components in this game object
        void sendMessage(Message m);

    private:
        //id to keep track of components in the manager
        const int id;

        //Pointers to components in the game object manager
        std::vector<Component*> components;
}

GameObjectSınıf onun bileşenleridir bilir ve onlara mesaj gönderebilirsiniz.

class Component {
    public:
        //Receives messages and acts accordingly
        virtual void handleMessage(Message m) = 0;

        virtual void update(Time dt) = 0;

    protected:
        //Calls GameObject's sendMessage
        void sendMessageToObject(Message m);

        //Calls GameObjectManager's sendMessage
        void sendMessageToWorld(Message m);
}

ComponentSınıf bileşenlerinin farklı türleri için sınıflar mesajları ve güncelleştirme nasıl işleneceğini uygulayabilir saf sanal böyledir. Ayrıca mesaj gönderebilir.

Şimdi sorun, bileşenlerin ve içindeki sendMessageişlevleri nasıl çağırabileceğiyle ilgili . İki olası çözüm buldum:GameObjectGameObjectManager

  1. ComponentBir işaretçi verin GameObject.

Bununla birlikte, oyun nesneleri bir vektörde olduğu için, işaretçiler hızla geçersiz hale gelebilir (Aynı şey vektör içinde de söylenebilir GameObject, ancak umarım bu sorunun çözümü de bunu çözebilir). Oyun nesnelerini bir diziye koyabilirdim, ancak boyut için rasgele bir sayı geçmem gerekecekti, bu da gereksiz yere yüksek ve bellek kaybı olabilir.

  1. Componentİşaretçisi verin GameObjectManager.

Ancak, bileşenlerin yöneticinin güncelleme işlevini çağırabilmesini istemiyorum. Bu proje üzerinde çalışan tek kişi benim, ancak potansiyel olarak tehlikeli kod yazma alışkanlığına girmek istemiyorum.

Kodumu güvenli ve önbellek dostu tutarken bu sorunu nasıl çözebilirim?

Yanıtlar:


6

İletişim modeliniz iyi görünüyor ve bu işaretçileri güvenli bir şekilde saklayabilseydiniz seçenek iyi olurdu. Bileşen depolaması için farklı bir veri yapısı seçerek bu sorunu çözebilirsiniz.

A std::vector<T>makul bir ilk seçim oldu. Ancak, kabın yineleyici geçersiz kılma davranışı bir sorundur. İstediğiniz şey, yinelemek için hızlı ve önbellek uyumlu olan ve öğeleri eklerken veya kaldırırken yineleyici stabilitesini koruyan bir veri yapısıdır .

Böyle bir veri yapısı oluşturabilirsiniz. Bağlantılı bir sayfa listesinden oluşur . Her sayfanın sabit bir kapasitesi vardır ve tüm öğeleri bir dizide tutar. Bu dizideki kaç öğenin etkin olduğunu göstermek için bir sayı kullanılır. Bir sayfa aynı zamanda ücretsiz bir liste (temizlenmiş girişlerin izin yeniden) ile atlamak için izin veren bir atlama listesi (vardır üzerinde iterating iken temizlenir girdileri.

Başka bir deyişle, kavramsal olarak şöyle bir şey:

struct Page {
   int count;
   int capacity;           // Optional if every page is a fixed size.
   T * m_storage;
   bool * m_skip;          // Skip list; can be bit-compressed.
   std::stack<int> m_free; // Can be replaced with a specialized stack.

   Page * next;
   Page * prior;           // Optional, allows reverse iteration
};

Bu veri yapısına yaratıcı olmayan bir kitap diyorum (çünkü yinelediğiniz sayfalar topluluğudur), ancak yapının başka isimleri vardır.

Matthew Bentley buna "koloni" diyor. Matthew'un uygulaması, bu tür yapılardaki daha tipik boole tabanlı atlama listesinden daha üstün olan, atlamalı sayma atlama alanı (MediaFire bağlantısı için özür diler, ancak Bentley'in belgeyi nasıl barındırdığıdır) kullanır. Bentley'nin kütüphanesi sadece üstbilgidir ve herhangi bir C ++ projesine girmeniz kolaydır, bu yüzden bunu kendiniz döndürmek yerine kullanmanız önerilir. Burada parladığım birçok incelik ve optimizasyon var.

Bu veri yapısı, öğeleri eklendikten sonra hiçbir zaman taşımadığından, o öğenin işaretçileri ve yineleyicileri, öğenin kendisi silinene kadar (veya kabın kendisi temizlenene kadar) geçerliliğini korur. Bitişik olarak ayrılmış öğelerin yığınlarını depoladığından, yineleme hızlı ve çoğunlukla önbellek uyumludur. Yerleştirme ve çıkarma mantıklıdır.

Mükemmel değil; kaptaki etkin rasgele noktalardan yoğun bir şekilde silinmesini ve ardından sonraki eklerin geri doldurulmuş öğeleri içermeden önce bu kabın üzerinde yinelenmesini içeren bir kullanım deseni ile önbellek tutarlılığını bozmak mümkündür. Bu senaryoda sık sık bulunuyorsanız, bir kerede potansiyel olarak büyük bellek bölgelerini atlayacaksınız. Ancak pratikte bu konteynerin senaryo için makul bir seçim olduğunu düşünüyorum.

Diğer yanıtları kapsamak için bırakacağım diğer yaklaşımlar, tanıtıcı tabanlı bir yaklaşım veya bir yuva haritası türünde yapı olabilir (tamsayı "değerleri" için bir tamsayı "anahtarları" ilişkisel diziniz varsa, değerler endekslerdir) bir ek dizilimde "index" ile erişerek bir vektör üzerinden yinelemenizi sağlayan bir destek dizisinde).


Selam! Son paragrafta bahsettiğiniz "koloniye" alternatifler hakkında daha fazla bilgi edinebileceğim herhangi bir kaynak var mı? Herhangi bir yere uygulanmış mı? Bir süredir bu konuyu araştırıyorum ve gerçekten ilgileniyorum.
Rinat Veliakhmedov

5

'Önbellek dostu' olmak, büyük oyunların sahip olduğu bir meşguliyettir . Bu benim için erken optimizasyon gibi görünüyor.


Bunu 'önbellek dostu' olmadan çözmenin bir yolu, nesnenizi yığın yerine yığın üzerinde oluşturmaktır: newnesneleriniz için kullanım ve (akıllı) işaretçiler. Bu şekilde, nesnelerinize başvurabileceksiniz ve referansları geçersiz kılınmayacak.

Daha önbellek dostu bir çözüm için, nesnelerin de / tahsisini kendiniz yönetebilir ve bu nesnelerin tutamaçlarını kullanabilirsiniz.

Temel olarak, programınızın başlangıcında, bir nesne yığın üzerinde bir bellek ayırır (buna MemMan diyelim), o zaman, bir bileşen oluşturmak istediğinizde MemMan'a X boyutunda bir bileşene ihtiyacınız olduğunu söylersiniz, ' sizin için ayırır, bir tutamaç oluşturur ve tahsisinde o tutamaç için nesne olduğu yerde dahili olarak tutar. Sapı döndürür ve nesne hakkında tutacağınız tek şey, asla hafızadaki konumuna bir işaretçi değildir.

Bileşene ihtiyacınız olduğu için MemMan'dan bu nesneye erişmesini isteyeceksiniz. Ama referansı saklamayın çünkü ....

MemMan'ın işlerinden biri, nesneleri hafızada birbirine yakın tutmaktır. Her birkaç oyun çerçevesinden sonra MemMan'a bellekteki nesneleri yeniden düzenlemesini söyleyebilirsiniz (veya nesneleri oluşturduğunuzda / sildiğinizde otomatik olarak yapabilir). Tutamaçtan belleğe konum haritasını güncelleyecektir. Kulplarınız her zaman geçerli olacaktır, ancak bellek alanına bir referans ( işaretçi veya referans ) tuttuysanız, yalnızca umutsuzluk ve ıssızlık görürsünüz.

Ders kitapları, hafızanızı bu şekilde yönetmenin en az 2 avantajı olduğunu söylüyor:

  1. nesneler bellekte birbirlerine yakın olduğu için daha az önbellek ıskalar ve
  2. biraz zaman aldığı söylenen işletim sistemine yapacağınız bellek de / tahsis çağrılarının sayısını azaltır .

MemMan'ı kullanma şeklinizin ve belleği dahili olarak nasıl düzenleyeceğinizin, bileşenlerinizi nasıl kullandığınıza bağlı olduğunu unutmayın. Bunları türlerine göre yineleyecekseniz bileşenleri oyun türlerine göre yinelerseniz, oyun nesnelerine göre yinelerseniz, bunlara yakın olduklarından emin olmanın bir yolunu bulmanız gerekir başka bir dayalı, vb ...

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.