Kodum boyunca tek seferlik bayrakları ve denetimleri önlemek için ne yapabilirim?


18

Hearthstone gibi bir kart oyunu düşünün .

Bazıları tek bir karta bile benzersiz olan çok çeşitli şeyler yapan yüzlerce kart var! Örneğin, oyuncunun dönüşlerini sadece 15 saniyeye indiren bir kart (Nozdormu olarak adlandırılır) var!

Çok çeşitli potansiyel etkilere sahip olduğunuzda, tüm kodlarınızda sihirli sayıları ve bir kerelik kontrolleri nasıl önlersiniz? PlayerTurnTime sınıfında "Check_Nozdormu_In_Play" yönteminden nasıl kaçınılır? Ve daha fazla efekt eklediğinizde , daha önce desteklemedikleri şeyleri desteklemek için çekirdek sistemleri yeniden düzenlemenize gerek kalmayacak şekilde kodu nasıl düzenleyebilirsiniz ?


Bu gerçekten bir performans sorunu mu? Yani, neredeyse hiçbir zaman modern CPU'larla çılgınca şeyler yapabilirsiniz ..
Jari Komppa

11
Performans sorunları hakkında bir şey kim söyledi? Gördüğüm temel sorun, her yeni kart yaptığınızda sürekli olarak tüm kodunuzu ayarlamanız gerekiyor.
jhocking

2
bu nedenle bir komut dosyası dili ekleyin ve her karta komut dosyası ekleyin.
Jari Komppa

1
Uygun bir cevap vermek için zaman yok, ancak oyuncunun dönüşlerini işleyen "PlayerTurnTime" sınıf kodu içinde örneğin Nozdormu kontrolü ve 15 saniye ayarlaması yapmak yerine, "PlayerTurnTime" sınıfını [class- ] işlevi belirli noktalardan dışarıdan sağlanır. Ardından Nozdormu kart kodu (ve aynı düzlemi etkilemesi gereken diğer tüm kartlar) bu ayarlama için bir işlev uygulayabilir ve gerektiğinde PlayerTurnTime sınıfına bu işlevi enjekte edebilir. Klasik Tasarım Desenleri kitabından strateji modeli ve bağımlılık enjeksiyonu hakkında okumak faydalı olabilir
Peteris

2
Kod alakalı bölüme geçici kontrolleri ekleyerek, bazı noktada merak zorunda olduğu en basit çözüm.
user253751

Yanıtlar:


12

Varlık bileşen sistemlerini ve olay mesajlaşma stratejilerini incelediniz mi?

Durum efektleri, bir OnCreate () yönteminde kalıcı etkilerini uygulayabilen, OnRemoved () içindeki etkilerini sona erdirebilecek ve gerçekleşen bir şeye tepki olarak ortaya çıkan efektleri uygulamak için oyun olay iletilerine abone olabilecek bir çeşit bileşen olmalıdır.

Etki kalıcı olarak koşulluysa (X dönüşleri sürer, ancak yalnızca belirli koşullar için geçerliyse), bu koşulları çeşitli aşamalarda kontrol etmeniz gerekebilir.

Ardından, oyununuzun varsayılan sihirli sayıları olmadığından emin olursunuz. Değiştirilebilecek her şeyin, istisnalar için kullanılan değişkenlerle sabit kodlu varsayılanlar yerine veriye dayalı bir değişken olduğundan emin olun.

Bu şekilde, dönüş uzunluğunun ne olacağını asla varsaymazsınız. Her zaman herhangi bir efektle değiştirilebilen ve muhtemelen daha sonra sona erdiğinde efekt tarafından geri alınabilen sürekli kontrol edilen bir değişkendir. Sihirli numaranızı varsayılana getirmeden önce hiçbir zaman istisna olup olmadığını kontrol etmezsiniz.


2
"Değiştirilebilecek her şeyin, özel durumlar için kullanılan değişkenlerle sabit kodlanmış varsayılanlar yerine veriye dayalı bir değişken olduğundan emin olun." - Ooh, bunu çok seviyorum. Sanırım bu çok yardımcı oluyor!
Sable Dreamer

"Kalıcı etkilerini uygula" konusunu açıklayabilir misiniz? TurnStarted'a abone olmak ve ardından Uzunluk değerini değiştirmek, kodu tartışılmaz hale getirir ve hatta daha da kötüsü tutarsız sonuçlar üretir (benzer etkiler arasında etkileşimde bulunurken)?
wondra

Sadece belirli bir zaman dilimini üstlenecek aboneler için. Dikkatli bir şekilde model olmalısınız. Mevcut dönüş süresinin oyuncu dönüş süresinden farklı olması iyi olabilir. PTT yeni bir dönüş oluşturmak için kontrol edilir. CTT kartlarla kontrol edilebilir. Bir etkinin geçerli saati artırması gerekiyorsa, zamanlayıcı kullanıcı arayüzü durumsuzsa doğal olarak uymalıdır.
RobStone

Soruyu daha iyi cevaplamak için. Başka hiçbir şey dönüş zamanını veya buna dayalı herhangi bir şeyi depolamaz. Daima kontrol edin.
RobStone

11

RobStone doğru yolda, ama detaylandırmak istedim çünkü silahlar ve büyüler için çok karmaşık bir etki sistemine sahip bir Roguelike olan Dungeon Ho!

Her kartta, efektin ne olduğunu, neyi hedeflediğini, nasıl ve ne kadar süreyle gösterebileceği şekilde tanımlanmış bir dizi efekt bulunmalıdır. Örneğin, "rakibe zarar verme" etkisi şöyle görünebilir;

Effect type: deal damage (enumeration, string, what-have-you)
Effect amount: 20
Source: my weapon
Target: opponent
Effect Cost: 20
Cost Type: Mana

Daha sonra, efekt patladığında, efektin işlenmesini sağlayan genel bir rutine sahip olun. Bir salak gibi, büyük bir vaka / anahtar ifadesi kullandım:

switch (effect_type)
{
     case DAMAGE:

     break;
}

Ancak bunu yapmanın çok daha iyi ve modüler bir yolu polimorfizmdir. Tüm bu verileri saran bir Effect sınıfı oluşturun, her efekt türü için bir alt sınıf oluşturun ve bu sınıfın sınıfa özgü bir onExecute () yöntemini geçersiz kılmasını sağlayın.

class Effect
{
    Object source;
    int amount;

    public void onExecute(Object target)
    {
          // Do nothing
    }
}

class DamageEffect extends Effect
{
    public void onExecute(Object target)
    {
          target.health -= amount;
    }
}

Böylece, temel bir Effect sınıfımız olacak, sonra onExecute () yöntemine sahip bir DamageEffect sınıfımız olacaktı, bu yüzden işleme kodumuzda sadece gideceğiz;

Effect effect = card.getActiveEffect();

effect.onExecute();

Oyunda ne olduğunu bilmenin bir Vektör / Dizi / bağlantılı liste / vb. herhangi bir nesneye (oyun alanı / "oyun" dahil) eklenmiş etkin efektlerin (tür efekt, temel sınıf), bu nedenle belirli bir efektin oyunda olup olmadığını kontrol etmek yerine, ve nesneleri çalıştırmasına izin verin. Bir nesneye bir efekt eklenmezse, oyunda değildir.

Effect effect;

for (int o = 0; o < objects.length; o++)
{
    for (int e = 0; e < objects[o].effects.length; e++)
    {
         effect = objects[o].effects[e];

         effect.onExecute();
    }
}

Ben de aynen böyle yaptım. Buradaki güzellik, aslında veri odaklı bir sisteme sahip olmanız ve mantığı efekt başına oldukça kolay bir şekilde ayarlamanızdır. Genellikle, efektin yürütme mantığında bazı durum kontrolleri yapmanız gerekir, ancak bu kontroller sadece söz konusu etki için olduğundan daha çok tutarlıdır.
manabreak

1

Bir avuç öneri sunacağım. Bazıları birbiriyle çelişiyor. Ama belki bazıları faydalıdır.

Listeleri bayraklara karşı düşünün

Bayrak şeyi yapıp yapmayacağınıza karar vermek için dünyayı tekrarlayabilir ve her öğenin bir bayrağını kontrol edebilirsiniz. Ya da yalnızca bayrak işini yapması gereken öğelerin bir listesini tutabilirsiniz.

Listeleri ve sıralamaları göz önünde bulundurun

Öğe sınıfınıza, isAThis ve isAThat'a boole alanları eklemeye devam edebilirsiniz. Veya {"isAThis", "isAThat"} veya {IS_A_THIS, IS_A_THAT} gibi dizelerin veya numaralandırma öğelerinin bir listesine sahip olabilirsiniz. Bu şekilde numaralandırmada (veya dize sabitlerinde) alan eklemeden yenilerini ekleyebilirsiniz. Alan eklemeyle ilgili gerçekten yanlış bir şey olmadığı için değil ...

İşlev işaretlerini düşünün

Bayrak veya numaralandırma listesi yerine, bu öğe için farklı bağlamlarda yürütülecek eylemlerin bir listesi olabilir. (Varlık-imsi ...)

Nesneleri düşünün

Bazı insanlar veri odaklı veya komut dosyası içeren veya bileşen varlık yaklaşımlarını tercih eder. Ancak eski moda nesneler hiyerarşileri de dikkate değer. Temel sınıf, “bu kartı B-dönüşü için oynat” veya başka bir şey gibi eylemleri kabul etmelidir. Daha sonra her tür kart uygun şekilde geçersiz kılabilir ve yanıt verebilir. Muhtemelen bir oyuncu nesnesi ve oyun nesnesi de vardır, bu nedenle oyun, (player-> isAllowedToPlay ()) {oyunu oyna…} gibi şeyler yapabilir.

Hata ayıklama yeteneğini düşünün

Bir kez bayrak alanıyla ilgili güzel bir şey, her öğenin durumunu aynı şekilde inceleyip yazdırabilmenizdir. Durum farklı türler veya bileşen torbaları veya işlev işaretçileriyle temsil ediliyorsa veya farklı listelerde bulunuyorsa, yalnızca öğenin alanlarına bakmak yeterli olmayabilir. Hepsi ödünç verilir.

Sonunda, yeniden düzenleme: Birim testlerini düşünün

Mimarinizi ne kadar genelleştirdiğiniz önemli değil, kapsamadığı şeyleri hayal edebileceksiniz. Sonra yeniden gözden geçirmeniz gerekecek. Belki biraz, belki çok.

Bunu daha güvenli hale getirmenin bir yolu, bir birim testler yapmaktır. Bu şekilde, altındaki işlevleri (belki de çok fazla!) Yeniden düzenlemenize rağmen mevcut işlevselliğin hala çalıştığından emin olabilirsiniz. Her birim testi genellikle şöyle görünür:

void test1()
{
   Game game;
   game.addThis();
   game.setupThat(); // use primary or backdoor API to get game to known state

   game.playCard(something something).

   int x = game.getSomeInternalState;
   assertEquals(“did it do what we wanted?”, x, 23); // fail if x isn’t 23
}

Gördüğünüz gibi, oyundaki (veya oynatıcı, kart & c) üst düzey API çağrılarını sabit tutmak, birim test stratejisinin anahtarıdır.


0

Her bir kartı ayrı ayrı düşünmek yerine, efekt kategorileri açısından düşünmeye başlayın ve kartlar bu kategorilerden birini veya daha fazlasını içerir. Örneğin, bir turdaki süreyi hesaplamak için oyundaki tüm kartlar arasında geçiş yapabilir ve bu kategoriyi içeren her kartın "dönüş süresini değiştir" kategorisini kontrol edebilirsiniz. Daha sonra her kart, karar verdiğiniz kurallara göre dönüş süresini artırır veya üzerine yazar.

Bu, aslında her "kart" nesnesinin bir grup efekt bileşeni için basit bir kap olduğu mini bileşenli bir sistemdir.


Kartlar ve gelecek kartlar da hemen hemen her şeyi yapabildiğinden, her kartın bir senaryo taşımasını beklerdim. Hala bunun gerçek bir performans sorunu olmadığından eminim ..
Jari Komppa

4
ana yorumlara göre: Kimse (sizden başka) performans sorunları hakkında bir şey söylemedi. Alternatif olarak tam senaryo yazımına gelince, bunu bir cevapta detaylandırın.
jhocking
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.