Sıra tabanlı bir oyunda varlık bileşenli bir oyun kasasını nasıl ilerletebilirim?


9

Şimdiye kadar kullandığım varlık bileşen sistemleri çoğunlukla Java'nın artemisi gibi çalıştı:

  • Bileşenlerdeki tüm veriler
  • Yalnızca belirli sistemin ilgilendiği bileşenleri içeren her bir varlık üzerinde yinelenen durum bilgisi olmayan bağımsız sistemler (en azından başlatma sırasında girdi gerektirmedikleri ölçüde) yineleme
  • Tüm sistemler varlıklarını bir kene işliyor, sonra her şey yeniden başlıyor.

Şimdi bunu ilk kez bir sıra tabanlı oyuna, oyunun ilerleyebilmesi için birbiri ile ilişkili olarak belirli bir sırada gerçekleşmesi gereken tonlarca olay ve yanıtla uygulamaya çalışıyorum. Bir örnek:

Oyuncu A kılıçtan hasar alır. Buna yanıt olarak A'nın zırhı alınan hasarı devreye sokar ve azaltır. A'nın hareket hızı da zayıflama sonucunda azalır.

  • Alınan hasar, tüm etkileşimi belirleyen şeydir
  • Zırh, oyuncuya hasar uygulanmadan önce hesaplanmalı ve gelen hasarlara uygulanmalıdır.
  • Hareket hızı azaltma, nihai hasar miktarına bağlı olduğundan, hasar gerçekten dağıtılana kadar bir birime uygulanamaz.

Olaylar başka olayları da tetikleyebilir. Zırh kullanarak kılıç hasarının azaltılması, kılıcın parçalanmasına neden olabilir (bu, hasar azaltma tamamlanmadan önce gerçekleşmelidir), bu da buna karşılık olarak ek olaylara neden olabilir, esasen olayların tekrarlanan bir değerlendirmesine neden olabilir.

Sonuçta, bu birkaç soruna yol açıyor gibi görünüyor:

  1. Birçok boşa harcanan işlem döngüsü: Çoğu sistemde (işleme gibi her zaman çalışan şeyler için tasarruf edin) çalışmak için "sıra" olmadığında yapılması gereken bir şey yoktur ve çoğu zaman oyunun girmesini beklemek için harcar geçerli bir çalışma durumu. Bu, bu tür her sistemi, oyuna daha fazla devlet eklendikçe büyümeye devam eden kontrollerle doldurur.
  2. Bir sistemin oyunda var olan varlıkları işleyip işleyemeyeceğini öğrenmek için, diğer ilişkisiz varlık / sistem durumlarını izlemenin bir yolunu bulmaları gerekir (hasar vermekten sorumlu olan sistemin zırhın uygulanmış olup olmadığını bilmesi gerekir). Bu, ya birden fazla sorumluluğu olan sistemleri karıştırır ya da her işlem döngüsünden sonra varlık koleksiyonunu taramak ve bir şeyler yapmanın uygun olduğu zaman onlara söyleyerek bir dinleyici kümesiyle iletişim kurmaktan başka bir amacı olmayan ek sistemlere gereksinim yaratır.

Yukarıdaki iki nokta, sistemlerin, bileşenlerindeki bayrakları kullanarak durum değiştiren aynı varlık kümesinde çalıştığını varsayar.

Bunu çözmenin başka bir yolu, oyun durumunu ilerletmek için tek bir sistem çalışması sonucunda bileşenler eklemek / kaldırmak (veya tamamen yeni varlıklar oluşturmak) olacaktır. Bu, bir sistem gerçekten eşleşen bir varlığa sahip olduğunda, onu işlemesine izin verildiğini bilir.

Ancak bu, sistemleri sonraki sistemlerin tetiklenmesinden sorumlu kılar ve tek bir sistem etkileşiminin sonucu olarak hatalar görünmeyeceğinden programların davranışları hakkında akıl yürütmeyi zorlaştırır. Yeni sistemler eklemek de zorlaşıyor, çünkü diğer sistemleri tam olarak nasıl etkilediklerini tam olarak bilmeden uygulanamıyorlar (ve önceki sistemlerin yeni sistemin ilgilendiği durumları tetiklemek için değiştirilmesi gerekebilir), biraz da ayrı sistemlere sahip olma amacını yenmek tek bir görevle.

Bu benim yaşamam gereken bir şey mi? Gördüğüm her ECS örneği gerçek zamanlıydı ve bu gibi durumlarda oyun başına bir yineleme döngüsünün nasıl çalıştığını görmek gerçekten çok kolay. Ve hala renderleme için ihtiyacım var, her şey olduğunda kendisinin birçok yönünü duraklatan sistemler için gerçekten uygun görünmüyor.

Oyun durumunu ileriye taşımak için bunun için uygun olan bir tasarım deseni var mı, yoksa sadece tüm mantığı döngüden çıkarmalı ve bunun yerine sadece gerektiğinde tetiklemeli miyim?


Bir olayın gerçekleşmesi için gerçekten anket yapmak istemezsiniz. Bir olay yalnızca gerçekleştiğinde gerçekleşir. Artemis, sistemlerin birbirleriyle iletişim kurmasına izin vermiyor mu?
Sidar

Sadece metotları kullanarak birleştirerek yapar.
Aeris130

Yanıtlar:


3

Buradaki tavsiyem, bileşen sistemi kullandığımız bir RPG projesindeki geçmiş deneyimlerden geliyor. Spagetti kodu olduğu için bu oyun kodunda çalışmaktan nefret ettiğimi söyleyeceğim. Bu yüzden burada çok fazla cevap sunmuyorum, sadece bir bakış açısı:

Bir oyuncuya kılıç hasarını vermek için tanımladığınız mantık ... Görünüşe göre bir sistem bunların hepsinden sorumlu olmalı.

Bir yerlerde bir HandleWeaponHit () işlevi var. İlgili zırhı elde etmek için oyuncu varlığının ArmorComponent bileşenine erişecektir. Belki de silahı parçalamak için saldıran silah varlığının WeaponComponent bileşenine erişecekti. Son hasarı hesapladıktan sonra, oyuncunun hız azaltmasını başarması için MovementComponent'e dokunacaktır.

Boşa harcanan işlem döngülerine gelince ... HandleWeaponHit () sadece gerektiğinde (kılıç vuruşunu tespit ettikten sonra) tetiklenmelidir.

Belki de yapmaya çalıştığım nokta şudur: kodda bir kırılma noktası koyabileceğiniz, vurabileceğiniz ve daha sonra bir kılıç vuruşu meydana geldiğinde çalışması gereken tüm mantığa adım atmaya devam edebileceğiniz bir yer istersiniz. Başka bir deyişle, mantık birden fazla sistemin tick () fonksiyonları boyunca dağılmamalıdır.


Bu şekilde yapılması, daha fazla davranış eklendikçe hit () işlevinin balon haline gelmesini sağlayacaktır. Diyelim ki, bir kılıç görüş açısı içinde bir hedefe (herhangi bir hedefe) her vurduğunda gülen bir düşman var. HandleWeaponHit bunu tetiklemekten gerçekten sorumlu olmalı mı?
Aeris130

1
Sıkıca genişlemiş bir savaş diziniz var, bu yüzden evet, etkileri tetiklemekten sorumludur. Her şey küçük sistemlere bölünmek zorunda değil , bu sistemin bunu ele almasına izin verin çünkü gerçekten "Savaş Sisteminiz" ve işliyor ... Savaş ...
Patrick Hughes

3

Bu bir yıllık bir soru ama şimdi ECS okurken ev yapımı oyunumla aynı zorluklarla karşılaşıyorum, bu yüzden biraz nekromani. Umarım bir tartışma veya en az birkaç yorumla sonuçlanır.

ECS kavramlarını ihlal edip etmediğinden emin değilim, ama ya:

  • Sistemlerin olay nesnelerini yayınlamasına / bunlara abone olmasına izin vermek için bir EventBus ekleyin (gerçek veriler, ancak sanırım bir bileşen değil)
  • Her ara durum için Bileşenler Oluşturma

Misal:

  • UserInputSystem [DamageDealerEntity, DamageReceiverEntity, Beceri / Silah kullanılan bilgisi] ile bir Saldırı olayı başlatır
  • CombatSystem abone olur ve DamageReceiver için kaçırma şansını hesaplar. Kaçırma başarısız olursa, aynı parametrelerle Hasar olayını tetikler
  • DamageSystem bu tür bir etkinliğe abone olur ve dolayısıyla tetiklenir
  • DamageSystem, Güç, BaseWeapon hasarı, türü vb. Kullanır ve onu [DamageDealerEntity, FinalOutgoingDamage, DamageType] ile yeni bir IncomingDamageComponent'e yazar ve hasar alıcı Varlık / Varlıklara ekler
  • DamageSystem bir Giden Hasar hesaplar
  • ArmorSystem tarafından tetiklenir, bir alıcı Varlığı alır veya IncomingDamageComponent'i almak için Varlıklar'daki bu IncomingDamage özelliğine göre arama yapar (sonuncusu yayılmış çoklu atamalar için muhtemelen daha iyi olabilir) ve uygulanan zırhı ve hasarı hesaplar. İsteğe bağlı olarak kılıç paramparça olayları tetikler
  • ArmorSystems, her bir varlıktaki IncomingDamageComponent öğesini kaldırır ve onu DamageReceivedComponent ile değiştirir ve HP'den yaraları ve yaralardan hız azaltmayı etkileyecek son hesaplanmış numaralarla değiştirir
  • ArmorSystems bir IncomingDamageCalculated olayı gönderir
  • Hız sistemi abone olur ve hızı yeniden hesaplar
  • HealthSystem abone olur ve gerçek HP'yi azaltır
  • vb
  • Bir şekilde temizle

Artıları:

  • Sistem birbirini tetikleyerek karmaşık zincir olayları için ara veriler sağlar
  • EventBus tarafından ayırma

Eksileri:

  • Bir şeyleri aktarmanın iki yolunu karıştırdığımı hissediyorum: olay parametreleri ve geçici Bileşenler. zayıf bir yer olabilir. Teoride şeyleri homojen tutmak için hiçbir veri olmadan sadece enum olaylarını tetikleyebilirdim, böylece Sistemler Varlık bileşenlerindeki zımni parametreleri en boyuta göre bulabilirdi ...
  • Potansiyel olarak ilgilenen tüm SystemsHave'in IncomingDamageCalculated'i temizleyip daha sonraki dönüşe izin verecek şekilde işleyip işlemediğini bilmiyorum. Belki CombatSystem'da bir tür kontroller ...

2

Sonunda yerleştiğim çözümü Yakovlev'inki gibi paylaşıyorum.

Temel olarak, bir olay sistemi kullandım, çünkü dönüşlerini mantığını takip etmeyi çok sezgisel buldum. Sistem, sıra tabanlı mantığa (oyuncu, canavarlar ve etkileşime girebilecekleri her şey) bağlı olan oyun içi birimlerden sorumlu hale geldi, renderleme ve giriş sorgulama gibi gerçek zamanlı görevler başka bir yere yerleştirildi.

Sistemler, bir olayı ve bir varlığı girdi olarak alan ve olayı aldığını bildiren bir onEvent yöntemi uygular. Her sistem belirli bir bileşen kümesine sahip olaylara ve varlıklara da abone olur. Sistemler için mevcut olan tek etkileşim noktası, olayları varlıklara göndermek ve belirli bir varlıktan bileşenleri almak için kullanılan varlık yöneticisi single'dır.

Varlık yöneticisi, gönderildiği varlıkla bağlantılı bir olay aldığında, olayı kuyruğun arkasına yerleştirir. Kuyrukta olaylar varken, en başta gelen olay alınır ve her iki olaya da abone olan ve olayı alan varlığın bileşen kümesiyle ilgilenen her sisteme gönderilir. Bu sistemler sırayla işletmenin bileşenlerini işleyebilir ve yöneticiye ek olaylar gönderebilir.

Örnek: Oyuncu hasar alır, bu nedenle oyuncu varlığına bir hasar olayı gönderilir. DamageSystem, sağlık bileşenine sahip herhangi bir kuruluşa gönderilen hasar olaylarına abone olur ve varlık bileşenindeki sağlığı olayda belirtilen miktarda azaltan bir onEvent (varlık, olay) yöntemine sahiptir.

Bu, bir zırh bileşenine sahip varlıklara gönderilen hasar olaylarına abone olan bir zırh sisteminin yerleştirilmesini kolaylaştırır. OnEvent yöntemi, olaydaki hasarı bileşendeki zırh miktarıyla azaltır. Bu, zırh sisteminin çalışması için hasar olayından önce hasar olayını işlemesi gerektiğinden, sistemlerin olay alma sırasını belirtmenin oyun mantığını etkilediği anlamına gelir.

Ancak bazen bir sistemin alıcı kuruluşun dışına çıkması gerekir. Eric Undersander'a yanıtımla devam etmek için, oyun haritasına erişen ve hasar alan varlığın x boşluğundaki FallsDownLaughingComponent ile varlıkları arayan bir sistem eklemek ve daha sonra bunlara bir FallDownLaughingEvent göndermek önemsiz olacaktır. Bu sistemin, hasar sisteminden sonra olayı alacak şekilde programlanması gerekir; hasar olayı bu noktada iptal edilmezse, hasar dağıtıldı.

Ortaya çıkan bir sorun, bazı yanıtların ek yanıtlar doğurabileceği göz önüne alındığında, yanıt olaylarının gönderildikleri sırayla işlenmesini sağlamaktı. Misal:

Oyuncu hareket eder, oyuncular varlığına bir hareket olayının gönderilmesini ve hareket sistemi tarafından alınmasını ister.

Kuyrukta: Hareket

Harekete izin verilirse, sistem oyuncuların konumunu ayarlar. Değilse (oyuncu bir engele geçmeye çalıştı), etkinliği iptal edilmiş olarak işaretler ve varlık yöneticisinin sonraki sistemlere göndermek yerine atmasına neden olur. Etkinlikle ilgilenen sistemler listesinin sonunda, oyuncunun sırayı karakterini hareket ettirmek için harcadığını ve sırası artık bittiğini doğrulayan TurnFinishedSystem var. Bu, player objesine bir TurnOver olayının gönderilmesine ve sıraya yerleştirilmesine neden olur.

Sırada: TurnOver

Şimdi oyuncunun bir tuzağa bastığını ve hasara neden olduğunu söyleyin. TrapSystem, hareket mesajını TurnFinishedSystem'dan önce alır, bu nedenle önce damage olayı gönderilir. Şimdi kuyruk bunun gibi görünüyor:

Sırada: Hasar, TurnOver

Şimdiye kadar her şey yolunda, hasar olayı işlenecek ve sonra dönüş sona erecek. Ancak, hasara yanıt olarak ek olaylar gönderilirse ne olur? Şimdi olay kuyruğu şöyle görünecektir:

Kuyrukta: Hasar, TurnOver, ResponseToDamage

Başka bir deyişle, hasar, herhangi bir hasara cevap verilmeden önce sona erecektir.

Bunu çözmek için olay gönderme iki yöntem kullanarak sona erdi: send (olay, varlık) ve yanıt (olay, eventToRespondTo, varlık).

Her olay, bir yanıt zincirindeki önceki olayların kaydını tutar ve yanıt () yöntemi her kullanıldığında, yanıtlanan olay (ve yanıt zincirindeki her olay), lle cevap ver. Başlangıç ​​hareketi olayının böyle bir olayı yoktur. Sonraki hasar yanıtında hareket olayı listesinde yer alır.

Bunun üzerine, birden çok olay kuyruğu içermek için değişken uzunlukta bir dizi kullanılır. Yönetici tarafından her olay alındığında, olay dizideki bir dizindeki yanıt zincirindeki olayların miktarıyla eşleşen bir kuyruğa eklenir. Böylece ilk hareket olayı [0] sırasında kuyruğa eklenir ve her ikisi de harekete yanıt olarak gönderildikleri için hasar ve ayrıca TurnOver olayları [1] 'de ayrı bir kuyruğa eklenir.

Zarar olayına yanıtlar gönderildiğinde, bu olaylar hem zarar olayının kendisini hem de hareketi içerecek ve bunları dizinde bir sıraya yerleştirecektir [2]. İndex [n] kuyruğunda olaylar olduğu sürece, bu olaylar [n-1] 'e geçmeden önce işlenecektir. Bu, aşağıdakilerin işleme sırasını verir:

Hareket -> Hasar [1] -> ResponseToDamage [2] -> [2] boş -> Dönüş [1] -> [1] boş -> [0] boş

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.