Oyun mimarisi / tasarım desenleri hakkında tavsiyeler


16

Bir süredir 2d RPG üzerinde çalışıyorum ve bazı kötü tasarım kararları aldığımı fark ettim. Özellikle bana sorun yaratan birkaç şey var, bu yüzden diğer insanların bunları aşmak için ne tür tasarımlar kullandığını veya kullanacağını merak ediyordum.

Küçük bir arka plan için, geçen yaz boş zamanımda üzerinde çalışmaya başladım. Başlangıçta oyunu C # 'da yapıyordum, ama yaklaşık 3 ay önce C ++' a geçmeye karar verdim. C ++ üzerinde iyi bir ele sahip olmak istedim, çünkü yoğun bir şekilde kullandığımdan beri bir süredir oldu ve bunun gibi ilginç bir projenin iyi bir motivasyon kaynağı olacağını düşündüm. Destek kütüphanesini kapsamlı bir şekilde kullanıyorum ve grafikler için SFML ve ses için FMOD kullanıyorum.

Yazılı bir kod biraz var, ama kazıma ve baştan düşünüyorum.

İşte sahip olduğum ve diğerlerinin onları çözme veya çözme şekli hakkında bazı görüşler almak istediğim önemli endişe alanları.

1. Döngüsel Bağımlılıklar Oyunu C # 'da yaparken, bu konuda endişelenmem gerekmiyordu çünkü bu bir sorun değil. C ++ 'a geçersek, bu oldukça büyük bir sorun haline geldi ve işleri yanlış tasarladığımı düşündürdü. Sınıflarımı nasıl ayıracağımı ve hala istediğimi yapmasını gerçekten hayal bile edemiyorum. İşte bir bağımlılık zincirine birkaç örnek:

Bir durum etkisi sınıfım var. Sınıf, efektleri bir karaktere uygulamak için çeşitli yöntemlere (Uygula / Uygulamayı Kaldır, Kene vb.) Sahiptir. Örneğin,

virtual void TickCharacter(Character::BaseCharacter* character, Battles::BattleField *field, int ticks = 1);

Bu fonksiyon, durum efekti uygulayan karakter her dönüş yaptığında çağrılır. Regen, Poison, vs. gibi efektleri uygulamak için kullanılır. Ancak BaseCharacter sınıfına ve BattleField sınıfına bağımlılıklar da getirir. Doğal olarak, BaseCharacter sınıfının şu anda üzerlerinde hangi durum etkilerinin etkin olduğunu takip etmesi gerekir, bu da döngüsel bir bağımlılıktır. Battlefield'ın savaşan tarafları takip etmesi gerekiyor ve parti sınıfında başka bir döngüsel bağımlılık getiren BaseCharacters listesi var.

2 - Etkinlikler

C # 'da karakterleri, savaş alanlarını vb. .) ve savaş alanı / grafik bileşenleri etkilerini güçlendirmek için bu delegelere bağlanırdı. C ++ 'da benzer bir şey yaptım. Açıkçası C # delegelerine doğrudan eşdeğer yok, bu yüzden bunun gibi bir şey yarattım:

typedef boost::function<void(BaseCharacter*, int oldvalue, int newvalue)> StatChangeFunction;

ve karakter sınıfımda

std::map<std::string, StatChangeFunction> StatChangeEventHandlers;

ne zaman karakterin stat değişti, ben yineleme ve harita üzerinde her StatChangeFunction çağırır. Çalışırken, bir şeyler yapmak için kötü bir yaklaşım olduğundan endişeliyim.

3 - Grafikler

Bu büyük bir şey. Kullandığım grafik kütüphanesi ile ilgili değil, daha çok kavramsal bir şey. C #, ben korkunç bir fikir olduğunu biliyorum benim sınıf bir sürü ile grafik birleştiğinde. Bunu yapmak istedim bu sefer farklı bir yaklaşım denedim.

Grafiklerimi uygulamak için, oyunla ilgili her şeyi bir dizi ekran olarak hayal ediyordum. Bir başlık ekranı, bir karakter durumu ekranı, bir harita ekranı, bir envanter ekranı, bir savaş ekranı, bir savaş GUI ekranı var ve temel olarak oyun grafiklerini oluşturmak için gerektiğinde bu ekranları üst üste koyabilirim. Etkin ekran ne olursa olsun, oyun girişine sahiptir.

Kullanıcı girişine göre ekranları iten ve pop yapan bir ekran yöneticisi tasarladım.

Örneğin, bir harita ekranındaysanız (Döşeme Haritası için bir giriş işleyici / görselleştirici) ve başlat düğmesine basarsanız, bir Ana Menü ekranını harita ekranının üzerine itmek ve haritayı işaretlemek için ekran yöneticisine bir çağrı yapılması gerekir. ekran çizilemez / güncellenmez. Oynatıcı menüde gezinir ve ekran yöneticisine yeni ekranları ekran yığınına itmek için ekran yöneticisine daha fazla komut verir, ardından kullanıcı ekranları değiştirdiğinde / iptal ettiğinde onları açar. Sonunda oyuncu ana menüden çıktığında, onu açıp harita ekranına geri döneceğim, çizileceğini / güncellendiğini ve oradan gitmesini söyledim.

Savaş ekranları daha karmaşık olurdu. Arka plan olarak hareket edecek bir ekranım, savaştaki her bir tarafı görselleştirmek için bir ekranım ve savaş için kullanıcı arayüzünü görselleştirmek için bir ekranım olacaktı. Kullanıcı arabirimi karakter olaylarına bağlanır ve bunları kullanıcı arabirimi bileşenlerinin ne zaman güncelleneceğini / yeniden çizileceğini belirlemek için kullanır. Son olarak, kullanılabilir bir animasyon komut dosyasına sahip her saldırı, ekran yığınından çıkmadan önce kendini canlandırmak için ek bir katman çağırır. Bu durumda, her katman sürekli olarak çekilebilir ve güncellenebilir olarak işaretlenir ve savaş grafiklerimi işleyen bir ekran yığını alıyorum.

Ekran yöneticisinin henüz mükemmel çalışmasını sağlayamasam da, biraz zaman ayırabileceğimi düşünüyorum. Bununla ilgili sorum şu: Bu hiç de faydalı bir yaklaşım mı? Kötü bir tasarımsa, ihtiyacım olacak tüm ekranları yapmak için çok fazla zaman harcamadan önce şimdi bilmek istiyorum. Oyununuz için grafikleri nasıl oluşturuyorsunuz?

Yanıtlar:


15

Genel olarak, listelediğiniz hiçbir şeyin sistemi hurdaya çıkarmanıza ve baştan başlamanıza neden olması gerektiğini söylemem. Bu, her programcının üzerinde çalıştıkları herhangi bir proje boyunca yolun yaklaşık% 50-75'ini yapmak istediği bir şeydir, ancak bu hiç bitmeyen bir gelişme döngüsüne ve hiçbir şeyi bitirmemeye yol açar. Bu amaçla, her bölümden bazıları geri beslenir.

  1. Bu bir sorun olabilir, ancak genellikle her şeyden daha fazla can sıkıcıdır. #Pragma'yı bir kez mi yoksa #ifndef MY_HEADER_FILE_H #define MY_HEADER_FILE_H ... #endif'i üstte mi yoksa .h dosyalarınızı çevreliyor musunuz? Bu şekilde .h dosyası her kapsamda yalnızca bir kez mi bulunur? Eğer öyleyse, tavsiyem daha sonra #include deyimlerini kaldırır ve derler, oyunu tekrar derlemek için gerekli olanları ekler.

  2. Bu tür sistemlerin hayranıyım ve yanlış bir şey görmüyorum. C # 'da bir Olay nedir genellikle bir Olay Sistemi veya Mesajlaşma Sistemi ile değiştirilir (daha fazla bilgi bulmak için burada soruları arayabilir). Buradaki anahtar, bir şeylerin olması gerektiğinde bunları minimumda tutmaktır, ki bu zaten yaptığınız gibi geliyor, burada en az endişe olmamalı.

  3. Bu da benim için doğru yolda görünüyor ve hem kişisel hem de profesyonel olarak kendi motorlarım için yaptığım şey bu. Bu, menü sistemini, nasıl ayarladığınıza bağlı olarak, kök menüsüne (oyun başlamadan önce) veya oyuncu kökünü 'kök' ekranı olarak gösteren bir durum sistemine dönüştürür.

Özetlemek gerekirse, karşılaştığınız şeyde layık bir şekilde yeniden başlayacak bir şey görmüyorum. Yolda daha resmi bir Etkinlik sisteminin değiştirilmesini isteyebilirsiniz, ancak bu zamanla gelecektir. Cyclic, tüm C / C ++ programcılarının sürekli atlamak zorunda kaldıkları bir engeldir ve grafikleri ayırmak için çalışmak mantıksal 'sonraki adımlar' gibi görünür.

Bu yardımcı olur umarım!


#ifdef, döngü içerme sorunlarını gidermez.
Komünist Ördek

Sadece döngüsel içerir izlemeden önce orada olmasını bekliyor ile benim temel kapsayan oldu. Kendini içeren bir dosyaya ihtiyaç duyan bir dosyanın aksine birden çok sembol tanımınız olduğunda başka bir balık su ısıtıcısı olabilir. (eğer içerdiği .HP dosyalarında değil .CPP dosyalarında ise ne tarif ettiğinden, birbirini bilen iki temel nesne ile tamam olmalıdır)
James

Tavsiye için teşekkürler :) Doğru yolda olduğumu bilmek sevindim
user127817

4

Üstbilgi dosyalarında sınıfları açıklayabileceğiniz ve aslında #cpp (ya da her neyse) dosyalarına dahil edebileceğiniz sınıfları ileri sürdüğünüz sürece döngüsel bağımlılıklarınız bir sorun olmamalıdır.

Etkinlik sistemi için iki öneri:

1) Şimdi kullanmakta olduğunuz kalıbı korumak istiyorsanız, std :: map yerine boost :: unordered_map dosyasına geçmeyi düşünün. Dizeler ile anahtar olarak eşleme yavaştır, özellikle de .NET, başlıkların altında bazı şeyleri hızlandırmaya yardımcı olmak için güzel şeyler yapar. Unordered_map komutunun kullanılması, dizeleri hash eder, böylece karşılaştırmalar genellikle daha hızlıdır.

2) boost :: sinyalleri gibi daha güçlü bir şeye geçmeyi düşünün. Bunu yaparsanız, boost :: signal :: izlenebilir öğesinden türeterek oyun nesnelerinizi izlenebilir hale getirmek gibi güzel şeyler yapabilirsiniz ve yıkıcıya, olay sisteminden manuel olarak kaydolmak yerine her şeyi temizlemeye özen gösterin. Ayrıca her bir yuvaya işaret eden birden fazla sinyale sahip olabilirsiniz (ya da tam tersi, tam isimlendirmeyi hatırlamıyorum), bu yüzden C # +='da a yapmaya çok benzer delegate. Boost :: sinyalleri ile ilgili en büyük sorun, derlenmesi gerektiğidir, sadece üstbilgiler değildir, bu nedenle platformunuza bağlı olarak kalkmak ve çalışmak bir acı olabilir.

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.