C ++ 'da bir oyun motoru nasıl organize edilir? Kalıtım kullanımım iyi bir fikir mi?


11

Hem oyun geliştirme hem de programlama konusunda yeni başlıyorum.

Bir oyun motorunun inşasında bazı prensipleri öğrenmeye çalışıyorum.
Basit bir oyun yaratmak istiyorum, oyun motorunu uygulamaya çalıştığım noktadayım.

Bu yüzden oyun motorumun bunları kontrol etmesi gerektiğini düşündüm:

- Moving the objects in the scene
- Checking the collisions
- Adjusting movements based on collisions
- Passing the polygons to the rendering engine

Nesnelerimi şöyle tasarladım:

class GlObject{
private:
    idEnum ObjId;
    //other identifiers
public:
    void move_obj(); //the movements are the same for all the objects (nextpos = pos + vel)
    void rotate_obj(); //rotations are the same for every objects too
    virtual Polygon generate_mesh() = 0; // the polygons are different
}

ve oyunumda 4 farklı nesne var: uçak, engel, oyuncu, mermi ve bunları şöyle tasarladım:

class Player : public GlObject{ 
private:
    std::string name;
public:
    Player();
    Bullet fire() const; //this method is unique to the class player
    void generate_mesh();
}

Şimdi oyun motorunda, örneğin çarpışma, nesne taşıma vb. İçin kontrol edebileceğim genel bir nesne listesine sahip olmak istiyorum, ancak oyun motorunun oynatıcıyı kontrol etmek için kullanıcı komutlarını almasını istiyorum ...

Bu iyi bir fikir mi?

class GameEngine{
private:
    std::vector<GlObject*> objects; //an array containg all the object present
    Player* hPlayer; //hPlayer is to be read as human player, and is a pointer to hold the reference to an object inside the array
public:
    GameEngine();
    //other stuff
}

GameEngine yapıcısı şu şekilde olacaktır:

GameEngine::GameEngine(){
    hPlayer = new Player;
    objects.push_back(hPlayer);
}

Bir işaretçi kullanıyorum fire(), Player nesnesine özgü olanı çağırmam gerektiğidir .

Benim sorum şu: bu iyi bir fikir mi? Burada miras kullanımım yanlış mı?



Gönderdikten sonra onları gördüm ve aslında kompozisyon harika bir fikir! Ben her zaman gerçek bir c ++ tasarım yerine sınıfları ile c kullanarak bitirmek için korku var!
Pella86

Kompozisyonun kalıtımdan çok daha fazla nedeni olduğunu düşünüyorum ... Cevabım sağladığı esneklik avantajları hakkında birkaç örnek veriyor. İyi bir uygulama örneği için bağladığım makalelere bakın.
Coyote

Yanıtlar:


12

'İyi' ile ne demek istediğine bağlı. Kesinlikle işi halledecek. Bol miktarda yukarı ve aşağı tarafı vardır. Motorunuz çok daha karmaşık olana kadar onları bulamazsınız. Konuyla ilgili tartışma için SO hakkındaki bu soruya bakın .

Her iki durumda da çok fazla fikir var, ancak motor kodunuz hala bu erken aşamadaysa, neden kötü olabileceğini açıklamak zor olacaktır.

Genel olarak, Scott Meyers'ın miras konusunda söyleyecek harika şeyleri var. Hala geliştirme aşamasındaysanız, devam etmeden önce bu kitabı (Etkili C ++) okuyun . Harika bir kitap ve şirketimizde çalışan kodlayıcılar için bir gereklilik. Ancak tavsiyeyi özetlemek gerekirse: Eğer bir sınıf yazıyorsanız ve miras kullanmayı düşünüyorsanız, sınıf ve atası arasındaki ilişkinin bir 'ilişki' olduğu kesinlikle açık olmalıdır. Yani, her B bir A'dır. Kalıtımı sadece B'nin sağladığı işlevselliğe erişim vermek için kolay bir yol olarak kullanmayın, bu şekilde ciddi mimari sorunlara yol açacaktır ve bunu yapmanın başka yolları da vardır.

Basitçe söylemek gerekirse; herhangi bir boyuttaki bir motor için, nesnelerin nadiren mirasın çalıştığı düzgün bir hiyerarşik yapıya düştüğünü hızlı bir şekilde bulacaksınız. Uygun olduğunda kullanın, ancak her şey için kullanma tuzağına düşmeyin - nesneleri birbiriyle ilişkilendirmenin diğer yollarını öğrenin.


İkinci olarak miras hakkındaki düşünceyi. Benim kişisel kuralım: başka bir şekilde yapmanız gerekmiyorsa veya bunu yapmak aptalca olmazsa yapmayın . Başka bir deyişle, vakaların% 99'unda kalıtım kötü bir fikirdir (en azından :)). Ayrıca, oyun mantığını (döndürme / taşıma) oluşturma işleminden (create_mesh ()) ayırmak iyi olabilir. Başka bir şey fire () - eylemler listesi ve genel eylem (my_action) işlevine sahip olmayı düşünün - bu şekilde tüm sınıflara yayılmış bazilyon farklı işlevlerle sonuçlanmayacaksınız. Her iki durumda da, sorunlarla karşılaştığınızda basit ve refactor'u acımasızca koruyun.
Gilead

8

Oyun nesnelerinizin (objelerin) mantıksal verileri ve işlevleri için kompozisyonun miras üzerinde kullanılmasını şiddetle tavsiye ederim. İyi bir başlangıç, bileşen tabanlı yaklaşım olacaktır. Bu mimarinin nasıl uygulanabileceğini ve Tony Hawk serisine nasıl faydalandığını anlatan bu Kovboy Programlama makalesinde iyi tanımlanmıştır .

Varlık sistemleri hakkındaki bu makale de ilk okuduğumda bana ilham verdi.

Kalıtım, polimorfizm için harika bir araçtır. Çok basit projeler dışında, varlıkları ve oyun mantıklarınızı yapılandırmanın bir yolu olarak kullanmaktan kaçınmalısınız. Kalıtımla giderseniz, kesinlikle ortak bir ebeveynden devralınamayan işlevleri uygulamak için aynı kodun kopyalarını çoğaltmak ve korumak zorunda kalacaksınız. Ve muhtemelen sınıflarınızı çok fazla kod çoğaltmaktan kaçınmak için en sık kullanılan mantık ve verilerle kirletmeye başlayacaksınız.

Bileşenler birçok durumda çok kullanışlıdır. Bunları kullanarak çok daha fazlasını başarabilirsiniz:

Varlık türlerinizle esneklik : Oyun geliştirmede bazı varlıkların aniden geliştiği zamanlar vardır ve oyun tasarımcısı geliştiricinin bunu uygulamak zorunda kalacağına karar verir. Kodu çoğaltmadan varolan bir davranışı eklemek yalnızca birkaç adım alır. Bileşen tabanlı sistemler, programcı olmayanlar tarafından daha fazla değişikliğin yapılmasına izin verir. Örneğin, bir sınıfla temsil etmek yerine her varlık türü, türü yapılandırmak için gereken tüm bileşen adlarını ve parametreleri içeren bir açıklama dosyasıyla temsil edilebilir. Bu, programcı olmayanların oyunu yeniden derlemeden değişiklikleri yapmasını ve test etmesini sağlar. Her bir varlık türü tarafından kullanılan bileşenleri eklemek, kaldırmak veya değiştirmek için açıklama dosyalarını düzenlemek yeterli olacaktır.

Belirli örneklerle ve belirli senaryolarla esneklik : Silahınızı veya eşyalarınızı donatmak için oynatıcınıza ihtiyaç duyduğunuz durumlar vardır, oynatıcınıza bir envanter bileşeni ekleyebilirsiniz. oyununuzun geliştirilmesi sırasında envantere sahip olmak için diğer varlıklara ihtiyacınız olacak (örneğin düşmanlar). Ve bir noktada senaryonuzda bir ağacın bir çeşit gizli eşya içermesi gereken bir durum var. Bileşenlerle, türü orijinal olarak tasarlanmamış varlıklara bile ek programlama çabası olmadan bir envanter bileşeni ekleyebilirsiniz.

Çalışma zamanında esneklik : Bileşenler, çalışma zamanında varlıkların davranışını değiştirmek için çok etkili bir yol sunar. Çalışma zamanında bileşenler ekleyip kaldırabilir ve böylece gerektiğinde davranışlar ve özellikler oluşturabilir veya kaldırabilirsiniz. Ayrıca, farklı veri türlerini birden fazla GPU'da paralel olarak (bazen) ayrı ayrı hesaplanabilen gruplara ayırmanıza izin verir.

Zaman içinde esneklik : Bileşenler, sürümler arasındaki uyumluluğu korumak için kullanılabilir. Örneğin, bir oyunun sürümleri arasında değişiklikler yapıldığında (her zaman değil) yeni davranışları ve değişiklikleri uygulayan yeni bileşenler ekleyebilirsiniz. Daha sonra oyun, yüklenen dosyaya, akran bağlı veya oynatıcının katıldığı sunucuya bağlı olarak varlıklarınıza bağlı doğru bileşenleri başlatır. Bu, yeni sürümün çıkış tarihlerini tüm platformlarda (Steam, Mac App Store, iPhone, Android ...) kontrol edemediğiniz senaryolarda son derece kullanışlıdır.

Daha iyi bir fikir edinmek için [bileşen tabanlı] ve [varlık sistemi] etiketli sorulara göz atın .


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.