Pek çok benzersiz silah / büyü / güç için kod nasıl yapılandırılır?


22

Ben Python kullanarak PyTL'i kullanarak (hala sadece metinle ilgilendiğim için PyGame yok) , FTL damarında "roguelike benzeri" bir oyun yaratan tecrübesiz bir programcıyım .

Oyunum, benzersiz yetenekler kazandıran çok sayıda silah (yeni başlayanlar için yaklaşık 50) içerecek. Nesne kodunun hem güçlü (silahların radikal olarak farklı etkilere sahip olmalarına izin verme açısından) hem de genişletilebilir bir şekilde nasıl yapılandırılacağını anlamak için mücadele ediyorum (böylece daha sonra kolayca bir klasöre bırakarak daha fazla silah ekleyebilirim) ).

İlk içgüdüm, bir BasicWeapon sınıfına sahip olmak ve bu sınıftan miras alan farklı silahlara sahip olmaktı. Bununla birlikte, bu benim için problemli görünüyor: ya BasicWeapon sınıfını temelde işe yaramaz hale getirecek barbarlar yapmak zorundayım (tüm silahların ortak özelliği olan tek özellik isim ve tip (tabanca, balta, vb.) Veya her birini tahmin etmek zorundayım. benzersiz bir etki şimdiye dek gelip bunları BasicWeapon'da kodlayacağım.

İkincisi açıkça imkansız, ancak eski hala çalışılabilir. Bununla birlikte, bu bana şu soruyu bırakıyor: Bireysel silahların kodunu nereye koyacağım?

Plasmarifle.py, rocketlauncher.py, swarmofbees.py, vb. Gibi öğeler oluşturup hepsini oyunun içe aktarabileceği bir klasöre mi bırakıyorum?

Ya da her bir silah için benzersiz bir kod içeren bir veritabanı tarzı dosyaya (belki de bir Excel elektronik tablosu gibi basit bir şey) sahip olmanın bir yolu var mı - eval / exec başvurmaya gerek kalmadan?

İkinci çözüm (veritabanı) açısından, mücadele ettiğim temel meselenin, kod ve veri arasındaki ayrımı korumanın istendiğini anladığım halde, silahların "kod" arasındaki çizgiyi bulanıklaştırdığını hissediyorum. ve biraz "veri"; oyunda bulunabilecek, hangi anlamda veri gibi olduklarını gösteren çok çeşitli benzer şeyleri temsil ediyorlar, ancak çoğu, başka bir şeyle paylaşılmayan en azından bazı benzersiz kodlar gerektirecek, bu da doğal olarak, kodu.

Bu sitede başka bir yerde bulduğum kısmi bir çözüm, BasicWeapon sınıfına bir sürü boş yöntem - on_round_start (), on_attack (), on_move () etc - ve her silah için bu yöntemleri geçersiz kılmayı önerir. Savaş döngüsünün ilgili aşamasında, oyun her karakterin silahı için uygun yöntemi arayacak ve sadece tanımlanmış yöntemleri olanları gerçekten bir şeyler yapacak. Bu yardımcı olur, ancak hala her silahın kodunu ve / veya verilerini nereye koymam gerektiğini söylemiyor.

Bir çeşit yarım veri, yarım kod chimera olarak kullanabileceğim farklı bir dil veya araç var mı? Tamamen iyi bir programlama pratiği yapıyor muyum?

Ben olmayan tepkiler seviniriz böylece OOP Benim anlayış, en iyi kabataslak çok bilgisayar bilimi-y.

EDIT: Vaughan Hilts, aşağıdaki yazısında, esasen bahsettiğim şeyin veri odaklı programlama olduğunu açıkça ortaya koydu. Sorumun özü şudur: veri odaklı bir tasarımı, verilerin ana program kodunu değiştirmeden yeni şeyler yapabilmesini sağlayacak şekilde kodlar içerebilecek şekilde nasıl uygulayabilirim?



@ Byte56 İlgili; ancak OP'nin kaçınmaya çalıştığı şeyin bu olduğunu düşünüyorum. Bence daha veri odaklı bir yaklaşım bulmaya çalışıyorlar. Yanlışsam düzelt.
Vaughan Hilts

Daha veri odaklı bir yaklaşım bulmaya çalıştıklarını kabul ediyorum. Özellikle, bu soruya Josh'un cevap gibi: gamedev.stackexchange.com/a/17286/7191
Michaelhouse

Ah, bunun için üzgünüm. :) "Kabul edilen cevabı" okuma konusunda kötü bir alışkanlığım var.
Vaughan Hilts

Yanıtlar:


17

Oyununuz tamamen beklenmeyen ve / veya çekirdeğe yönelik bir prosedür oluşturmayacaksa, neredeyse kesinlikle veri odaklı bir yaklaşım istiyorsunuz .

Temel olarak, bu, silahlarınızla ilgili bilgilerin, bir biçimlendirme dilinde veya seçtiğiniz dosya biçiminde saklanmasını içerir. XML ve JSON, hızlı bir başlangıç ​​yapmaya çalışıyorsanız, karmaşık editörlere ihtiyaç duymadan düzenlemeyi oldukça basit hale getirmek için kullanılabilecek iyi ve okunabilir seçeneklerdir. ( Ve Python da XML'i çok kolay bir şekilde ayrıştırabilir! ) 'İlgili' 'güç', 'savunma', 'maliyet' ve 'istatistik' gibi özelliklere sahip olacaksınız. Verilerinizi yapılandırma şekliniz size bağlı olacaktır.

Bir silahın bir durum efekti eklemesi gerekiyorsa, ona bir Durum efekti düğümü verin ve ardından başka bir veri odaklı nesne aracılığıyla bir durum efektinin etkilerini belirtin. Bu, kodunuzu belirli oyuna daha az bağımlı hale getirecek ve oyununuzu düzenlemeye ve test etmeye önemsiz hale getirecektir. Her zaman yeniden derlemek zorunda kalmamak da bir bonus.

Ek okuma aşağıda mevcuttur:


2
Bileşenlerin senaryolarla okunduğu bir bileşen tabanlı sistem gibi. Bunun gibi: gamedev.stackexchange.com/questions/33453/…
MichaelHouse

2
Ve siz bu sırada, bu verilerin bir betiğini oluşturun, böylece yeni silahlar, ana kod değişikliği olmadan yeni şeyler yapabilir.
Patrick Hughes

@ Vaughan Hilts: teşekkür ederim, veri odaklı, tam olarak ihtiyacım olan sezgisel olarak anladığım gibi görünüyor. Bir süre daha soruyu açık bırakmaya devam ediyorum, hala cevaplara ihtiyacım var ama muhtemelen bunu en iyi cevap olarak seçecek.
henrebotha

@Patrick Hughes: tam olarak istediğim bu! Bunu nasıl yaparım? Bana basit bir örnek veya öğretici gösterebilir misiniz?
henrebotha

1
Öncelikle, motorunuzda bir komut dosyası motoruna ihtiyacınız var, birçok kişi LUA'yı seçiyor, bu da efektler ve istatistikler gibi oyun sistemlerine erişiyor. Daha sonra, nesnelerinizi zaten bir veri tanımından yeniden yarattığınız için, yeni nesneniz etkinleştirildiğinde, motorunuzun çağırdığı komut dosyasını gömebilirsiniz. MUD'ların eski günlerinde buna "proc" denir (Süreç için kısa). Zor kısım, motordaki oyun özelliklerinizi dışarıdan ve yeterli özelliklerle çağrılabilecek kadar esnek hale getirmektir.
Patrick Hughes,

6

(Yorum yapmak yerine yanıtı gönderdiğim için üzgünüm ama henüz bir sorum yok.)

Vaughan'ın cevabı harika, ama iki sentimi de eklemek istiyorum.

XML veya JSON kullanmak ve çalışma zamanında ayrıştırmak istemenizin ana nedenlerinden biri, kodu yeniden derlemek zorunda kalmadan yeni değerleri değiştirmek ve denemek. Python yorumlandığı için ve bence oldukça okunaklı bir şekilde, ham verileri bir sözlükte ve her şeyi organize edilmiş bir dosyada bulabilirsiniz:

weapons = {
           'megaLazer' : {
                          'name' : "Mega Lazer XPTO"
                          'damage' : 100
                       },
           'ultraCannon' : {
                          'name' : "Ultra Awesome Cannon",
                          'damage' : 200
                       }
          }

Bu şekilde sadece dosyayı / modülü içe aktarır ve normal bir sözlük olarak kullanırsınız.

Komut dosyaları eklemek istiyorsanız, Python ve 1. sınıf fonksiyonların dinamik doğasını kullanabilirsiniz. Böyle bir şey yapabilirsin:

def special_shot():
    ...

weapons = { 'megalazer' : { ......
                            shoot_gun = special_shot
                          }
          }

Buna rağmen veri odaklı tasarıma karşı olacağına inanıyorum. % 100 DDD olması için, belirli bir silahın kullanacağı fonksiyonların ve kodların ne olacağını belirten bilgileriniz olacaktı. Bu şekilde, verileri işlevsellik ile karıştırmadığınız için DDD'yi bozmazsınız.


Teşekkür ederim. Sadece basit bir kod örneği görmek tıklamasına yardımcı oldu.
henrebotha

1
Güzel cevap için ve +1 yorum yapmak için yeterli temsilci olmanız için. ;) Hoşgeldiniz.
ver

4

Veri Odaklı Tasarım

Geçenlerde kod incelemesine bu soru gibi bir şey gönderdim .

Bazı öneri ve iyileştirmelerden sonra, sonuç bir sözlüğe (veya JSON'a) dayanarak silah yaratmada biraz esneklik sağlayacak basit bir kod oldu. Veriler çalışma zamanında yorumlanır ve basit bir doğrulama, Weapontüm komut dosyası yorumlayıcısına güvenmeksizin sınıf tarafından yapılır .

Veri Odaklı Tasarım, Python'un yorumlanmış bir dil olmasına rağmen (hem kaynak hem de veri dosyaları onları yeniden derlemeye gerek kalmadan düzenlenebilir), sunduğunuz gibi durumlarda doğru olanı sesler. Bu soru kavramı, artıları ve eksileri hakkında daha fazla ayrıntıya giriyor. Ayrıca Cornell Üniversitesi hakkında bu konuda güzel bir sunum .

Python aslında veri x motor etkileşimini ve genel olarak komut dosyasını işlemek için bir komut dosyası dili (LUA gibi) ve genel olarak komut dosyası işlemesi için muhtemelen bir komut dosyası dili ve Cd gibi diğer dillerle karşılaştırıldığında, Python gerçekten yapabilir kendi üzerindeki tüm (standart dikkate alınarak dictdeğil, aynı zamanda weakrefözellikle kaynak yükleme ve önbelleğe alma için, ikincisi).

Bununla birlikte, bağımsız bir geliştirici, bu makalede önerildiği gibi, veri odaklı yaklaşımı uç noktalarına taşımayabilir :

Veri odaklı tasarım hakkında ne kadarım? Bir oyun motorunun tek bir oyuna özgü kod satırı içermesi gerektiğini düşünmüyorum. Bir değil. Kodlanmış silah türü yok. Kodlanmış HUD düzeni yok. Kodlanmış birim AI yok. Nada. Zip. Sıfır.

Belki, Python ile, hem üretkenlik hem de genişletilebilirlik hedefiyle hem nesne yönelimli hem de veri odaklı yaklaşımın en iyilerinden yararlanılabilir.

Basit örnek işleme

Kod incelemesinde tartışılan özel durumda, bir sözlük hem "statik nitelikler" hem de yorumlanacak mantığı - silahın herhangi bir şartlı davranışa sahip olması halinde saklayacaktır.

Aşağıdaki örnekte bir kılıç, 'antipaladin' sınıfının karakterlerinin elinde bazı yetenek ve istatistiklere sahip olmalı ve diğer karakterler tarafından kullanıldığında daha düşük istatistiklerle sonuçlanmamalı):

WEAPONS = {
    "bastard's sting": {
        # magic enhancement, weight, value, dmg, and other attributes would go here.
        "magic": 2,

        # Those lists would contain the name of effects the weapon provides by default.
        # They are empty because, in this example, the effects are only available in a
        # specific condition.    
        "on_turn_actions": [],
        "on_hit_actions": [],
        "on_equip": [
            {
                "type": "check",
                "condition": {
                    'object': 'owner',
                    'attribute': 'char_class',
                    'value': "antipaladin"
                },
                True: [
                    {
                        "type": "action",
                        "action": "add_to",
                        "args": {
                            "category": "on_hit",
                            "actions": ["unholy"]
                        }
                    },
                    {
                        "type": "action",
                        "action": "add_to",
                        "args": {
                            "category": "on_turn",
                            "actions": ["unholy aurea"]
                        }
                    },
                    {
                        "type": "action",
                        "action": "set_attribute",
                        "args": {
                            "field": "magic",
                            "value": 5
                        }
                    }
                ],
                False: [
                    {
                        "type": "action",
                        "action": "set_attribute",
                        "args": {
                            "field": "magic",
                            "value": 2
                        }
                    }
                ]
            }
        ],
        "on_unequip": [
            {
                "type": "action",
                "action": "remove_from",
                "args": {
                    "category": "on_hit",
                    "actions": ["unholy"]
                },
            },
            {
                "type": "action",
                "action": "remove_from",
                "args": {
                    "category": "on_turn",
                    "actions": ["unholy aurea"]
                },
            },
            {
                "type": "action",
                "action": "set_attribute",
                "args": ["magic", 2]
            }
        ]
    }
}

Sınama amacıyla, basit Playerve Weaponsınıflar oluşturdum : silahı tutan / donatacak ilk (böylece koşullu on_equip ayarını çağırarak) ve ikincisi, bir Weaponbaşlatma sırasında tartışma . Uygun oyun sınıfları tasarımını yansıtmazlar, ancak verileri test etmek için hala yararlı olabilirler:

class Player:
    """Represent the player character."""

    inventory = []

    def __init__(self, char_class):
        """For this example, we just store the class on the instance."""
        self.char_class = char_class

    def pick_up(self, item):
        """Pick an object, put in inventory, set its owner."""
        self.inventory.append(item)
        item.owner = self


class Weapon:
    """A type of item that can be equipped/used to attack."""

    equipped = False
    action_lists = {
        "on_hit": "on_hit_actions",
        "on_turn": "on_turn_actions",
    }

    def __init__(self, template):
        """Set the parameters based on a template."""
        self.__dict__.update(WEAPONS[template])

    def toggle_equip(self):
        """Set item status and call its equip/unequip functions."""
        if self.equipped:
            self.equipped = False
            actions = self.on_unequip
        else:
            self.equipped = True
            actions = self.on_equip

        for action in actions:
            if action['type'] == "check":
                self.check(action)
            elif action['type'] == "action":
                self.action(action)

    def check(self, dic):
        """Check a condition and call an action according to it."""
        obj = getattr(self, dic['condition']['object'])
        compared_att = getattr(obj, dic['condition']['attribute'])
        value = dic['condition']['value']
        result = compared_att == value

        self.action(*dic[result])

    def action(self, *dicts):
        """Perform action with args, both specified on dicts."""
        for dic in dicts:
            act = getattr(self, dic['action'])
            args = dic['args']
            if isinstance(args, list):
                act(*args)
            elif isinstance(args, dict):
                act(**args)

    def set_attribute(self, field, value):
        """Set the specified field with the given value."""
        setattr(self, field, value)

    def add_to(self, category, actions):
        """Add one or more actions to the category's list."""
        action_list = getattr(self, self.action_lists[category])

        for action in actions:
            if action not in action_list:
                action_list.append(action)

    def remove_from(self, category, actions):
        """Remove one or more actions from the category's list."""
        action_list = getattr(self, self.action_lists[category])

        for action in actions:
            if action in action_list:
                action_list.remove(action)

Gelecekteki gelişmelerle birlikte, bunun bir gün dinamik bir ustalık sistemine sahip olmamı, tüm silahlar yerine silah bileşenlerini işlememi sağlayacağımı umuyorum ...

Ölçek

  1. A karakteri bir silah seçer, donatır (istatistiklerini yazdırırız), sonra düşürür;
  2. B karakteri aynı silahı alır, onu donatır (ve nasıl farklı olduklarını göstermek için istatistiklerini tekrar basarız).

Bunun gibi:

def test():
    """A simple test.

    Item features should be printed differently for each player.
    """
    weapon = Weapon("bastard's sting")
    player1 = Player("bard")
    player1.pick_up(weapon)
    weapon.toggle_equip()
    print("Enhancement: {}, Hit effects: {}, Other effects: {}".format(
        weapon.magic, weapon.on_hit_actions, weapon.on_turn_actions))
    weapon.toggle_equip()

    player2 = Player("antipaladin")
    player2.pick_up(weapon)
    weapon.toggle_equip()
    print("Enhancement: {}, Hit effects: {}, Other effects: {}".format(
        weapon.magic, weapon.on_hit_actions, weapon.on_turn_actions))

if __name__ == '__main__':
    test()

Yazdırmalı:

Bir ozan için

Geliştirme: 2, Hit efektleri: [], Diğer efektler: []

Bir antipaladin için

Geliştirme: 5, Hit etkileri: ['unholy'], Diğer etkiler: ['unholy aurea']

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.