Oyunlarda çizim ve mantığın ayrılması


11

Şimdi oyun geliştirmeyle uğraşmaya başlayan bir geliştiriciyim. Ben bir Net adamım, bu yüzden XNA ile uğraştım ve şimdi iPhone için Cocos2d ile oynuyorum. Benim sorum gerçekten daha genel.

Diyelim ki basit bir Pong oyunu inşa ediyorum. Bir Balldersim ve bir Paddledersim olurdu . İş dünyasının gelişiminden gelen ilk içgüdüm, bu sınıfların hiçbirinde çizim veya girdi işleme koduna sahip olmamaktır.

//pseudo code
class Ball
{
   Vector2D position;
   Vector2D velocity;
   Color color;
   void Move(){}
}

Top sınıfındaki hiçbir şey girişi işlemez veya çizim ile ilgilenmez. Daha sonra , topu yenileyecek başka bir sınıfım, Gamesınıfım ya da Scene.m(Cocos2d'de) olurdu ve oyun döngüsü sırasında, topu gerektiği gibi manipüle ederdi.

Şey, hem XNA hem de Cocos2d için birçok öğreticide, böyle bir desen görüyorum:

//pseudo code
class Ball : SomeUpdatableComponent
{
   Vector2D position;
   Vector2D velocity;
   Color color;
   void Update(){}
   void Draw(){}
   void HandleInput(){}
}

Sorum şu, bu doğru mu? İnsanların oyun geliştirmede kullandıkları model bu mu? Ball sınıfımın her şeyi yapması bir şekilde alıştığım her şeye aykırı. Dahası, Ballnasıl hareket edeceğimi bildiğim bu ikinci örnekte, çarpışma algılamasını nasıl yapabilirim Paddle? Hakkında Ballbilgi sahibi olmak gerekir mi Paddle? İlk örneğimde, Gamesınıfın hem Ballve hem de referanslarına sahip olacak Paddleve her ikisini de bir CollisionDetectionyöneticiye veya bir şeye gönderecekti , ancak her bir bileşen her şeyi kendi başına yapıyorsa, çeşitli bileşenlerin karmaşıklığıyla nasıl başa çıkabilirim? (Umarım anlam ifade ederim .....)


2
Ne yazık ki birçok oyunda gerçekten böyle yapılır. Bunu en iyi uygulama olarak kabul etmem. Okuduğunuz öğreticilerin çoğu yeni başlayanlara yönelik olabilir ve bu nedenle okuyucuya model / görünüm / denetleyici veya boyunduruk kalıplarını açıklamaya başlamayacaklardır.
tenpn

@tenpn, yorumunuz bunun iyi bir uygulama olduğunu düşünmüyor gibi görünüyor, daha fazla açıklayabilir misiniz acaba? Her zaman muhtemelen her tür için çok özel kod içeren yöntemler nedeniyle en uygun düzenleme olduğunu düşündüm; ama kendi kendime çok az tecrübem var, bu yüzden düşüncelerinizi duymak isterim.
sebf

1
@sebf veri odaklı tasarıma sahip değilseniz ve nesne yönelimli tasarımı seviyorsanız çalışmaz. Bob Amca'nın SOLID prensiplerine
tenpn

@tenpn. İçinde bağlantılı olan bu makale ve belge çok ilginç, teşekkürler!
sebf

Yanıtlar:


10

Sorum şu, bu doğru mu? İnsanların oyun geliştirmede kullandıkları model bu mu?

Oyun geliştiricileri işe yarayan her şeyi kullanma eğilimindedir. Titiz değil, ama hey, gemi geliyor.

Ball sınıfımın her şeyi yapması bir şekilde alıştığım her şeye aykırı.

Yani, orada ne var için daha genel bir deyim handleMessages / update / draw. Bir oyun varlığı mesajların yoğun kullanımını (beklediğiniz gibi artıları ve eksileri vardır) kullanan sistemlerde, önem verdiği mesajları alır, bu mesajlar üzerinde mantık yapar ve sonra kendini çizer.

Bu draw () çağrısının gerçekte varlığın putPixel veya kendi içindeki her şeyi çağırdığı anlamına gelmeyebileceğini unutmayın; bazı durumlarda, daha sonra çizimden sorumlu kod tarafından sorgulanan verilerin güncellenmesi anlamına gelebilir. Daha gelişmiş durumlarda, bir oluşturucunun maruz kaldığı yöntemleri çağırarak kendisini "çizer". Şu anda üzerinde çalıştığım teknolojide, nesne renderer çağrılarını kullanarak kendini çiziyordu ve daha sonra render iş parçacığının her karesi tüm nesneler tarafından yapılan tüm çağrıları organize ediyor, durum değişiklikleri gibi şeyler için optimize ediyor ve sonra her şeyi ortaya çıkarıyor (tümü Bu, oyun mantığından ayrı bir iş parçacığında gerçekleşir, bu nedenle eşzamansız oluşturma elde ederiz).

Sorumluluk delegasyonu programcıya bağlıdır; basit bir pong oyunu için, her nesnenin kendi çizimini (düşük seviyeli çağrılarla tamamlanmış) tamamen ele alması mantıklıdır. Bu bir stil ve bilgelik meselesi.

Dahası, Topumun nasıl hareket edeceğini bildiği bu ikinci örnekte, Raket ile çarpışma tespitini nasıl ele alırım? Topun Kürek hakkında bilgi sahibi olması gerekir mi?

Bu nedenle, sorunu burada çözmenin doğal bir yolu, her nesnenin diğer nesneleri nesnelere çarpışmaları kontrol etmek için bir tür parametre olarak almasını sağlamaktır. Bu kötü ölçeklenir ve garip sonuçlar doğurabilir.

Her sınıfın bir çeşit Collidable arabirimi uygulaması ve daha sonra oyununuzun güncelleme aşamasında tüm Collidable nesnelerin üzerinden geçen ve çarpışmaları işleyen, nesne konumlarını gerektiği gibi ayarlayan yüksek düzeyli bir kod parçasına sahip olmak.

Yine, oyununuzdaki farklı şeylerin sorumluluğunun nasıl devredilmesi gerektiğine dair bir fikir geliştirmeniz (veya karar vermeniz) gerekiyor. Rendering tek başına bir aktivitedir ve vakumdaki bir nesne tarafından idare edilebilir; çarpışma ve fizik genellikle yapamaz.

İlk örneğimde, Game sınıfının hem Top hem de Raket'e referansları olacak ve her ikisini de bazı CollisionDetection yöneticisine veya bir şeye gönderecekti, ancak her bir bileşen varsa, çeşitli bileşenlerin karmaşıklığıyla nasıl başa çıkacağım? her şey kendileri mi?

Bunu araştırmak için yukarıya bakın. Arabirimleri kolayca destekleyen bir dildeyseniz (ör. Java, C #), bu kolaydır. C ++ iseniz, bu ... ilginç olabilir. Ben bu sorunları çözmek için mesajlaşma kullanıyorum (sınıflar yapabildikleri iletileri işliyor, başkalarını görmezden geliyor), bileşen kompozisyonu gibi diğer insanlar.

Yine, neyin işe yaradığını ve sizin için en kolay olanı.


2
Oh, ve her zaman hatırla: YAGNI (Buna ihtiyacın olmayacak) burada gerçekten önemli. Mümkün olan her sorunu çözmek için ideal esnek sistemi bulmaya çok zaman harcayabilir ve hiçbir şey yapamazsınız. Yani, tüm olası sonuçlar için gerçekten çok oyunculu bir omurga istiyorsanız, CORBA'yı kullanırsınız, değil mi? :)
ChrisE

5

Son zamanlarda bir 'varlık sistemi' kullanarak basit bir Space Invadors oyunu yaptım. Nitelikleri ve davranışları son derece iyi ayıran bir örüntüdür. Tamamen anlamak için birkaç tekrar aldı, ancak birkaç bileşen tasarlandıktan sonra, mevcut bileşenlerinizi kullanarak yeni nesneler oluşturmak son derece basitleşiyor.

Bunu okumalısınız:

http://t-machine.org/index.php/2007/09/03/entity-systems-are-the-future-of-mmog-development-part-1/

Son derece bilgili bir adam tarafından sık sık güncellenir. Aynı zamanda somut kod örnekleri ile tek varlık sistemi tartışmasıdır.

Yinelemelerim şöyle:

İlk yinelemenin Adem'in tanımladığı gibi bir "EntitySystem" nesnesi vardı; ancak bileşenlerim hala yöntemlere sahipti - 'yenilenebilir' bileşenim bir paint () yöntemine sahipti ve konum bileşenim bir move () yöntemine vb. sahipti. bileşenleri ve bileşen güncellemeleri yürütme sipariş .... çok dağınık.

Bu yüzden geri döndüm ve T-machines blogunu tekrar okudum. Yorum dizilerinde çok fazla bilgi var - ve içlerinde bileşenlerin davranışları olmadığını vurguluyor - varlık sistemleri tarafından sağlanan davranışlar. Bu şekilde, bileşenler sistem yürütme sırasının genel sırası tarafından belirlendiği için, bileşenler arasında mesaj geçirmeniz ve bileşen güncellemeleri sipariş etmeniz gerekmez . Tamam. Belki bu çok soyut.

Neyse yineleme # 2 için bu blogdan topladım:

EntityManager - belirli türdeki bileşenleri içeren varlıklar için sorgulanabilen "veritabanı" bileşeni olarak işlev görür. Bu, hızlı erişim için bir bellek içi veritabanı tarafından da desteklenebilir ... daha fazla bilgi için t-makine bölüm 5'e bakın.

EntitySystem - Her sistem aslında sadece bir dizi entre üzerinde çalışan bir yöntemdir. Her sistem, bir işin işini yapmak için x, y ve z bileşenlerini kullanır. Böylece yöneticiyi x, y ve z bileşenlerine sahip varlıklar için sorgulayacak ve daha sonra bu sonucu sisteme ileteceksiniz.

Varlık - sadece bir kimlik, uzun gibi. Varlık, bir dizi bileşen örneğini birlikte bir 'varlık' olarak gruplayan şeydir.

Bileşen - bir dizi alan .... davranış yok! davranış eklemeye başladığınızda, basit bir Space Invadors oyununda bile dağınık olmaya başlar.

Düzenleme : bu arada, 'dt' son ana döngü çağrısından beri delta zamanı

Benim ana Invadors döngü budur:

Collection<Entity> entitiesWithGuns = manager.getEntitiesWith(Gun.class);
Collection<Entity> entitiesWithDamagable =
manager.getEntitiesWith(BulletDamagable.class);
Collection<Entity> entitiesWithInvadorDamagable = manager.getEntitiesWith(InvadorDamagable.class);

keyboardShipControllerSystem.update(entitiesWithGuns, dt);
touchInputSystem.update(entitiesWithGuns, dt);
Collection<Entity> entitiesWithInvadorMovement = manager.getEntitiesWith(InvadorMovement.class);
invadorMovementSystem.update(entitiesWithInvadorMovement);

Collection<Entity> entitiesWithVelocity = manager.getEntitiesWith(Velocity.class);
movementSystem.move(entitiesWithVelocity, dt);
gunSystem.update(entitiesWithGuns, System.currentTimeMillis());
Collection<Entity> entitiesWithPositionAndForm = manager.getEntitiesWith(Position.class, Form.class);
collisionSystem.checkCollisions(entitiesWithPositionAndForm);

İlk başta biraz garip görünüyor, ama inanılmaz derecede esnek. Optimize etmek de çok kolay; farklı bileşen türleri için, daha hızlı erişim sağlamak için farklı destek veri depolarına sahip olabilirsiniz. 'Form' sınıfı için, çarpışma tespiti için erişimi hızlandırmak üzere dörtlü ile destekleyebilirsiniz.

Ben senin gibiyim; Tecrübeli bir geliştiriciyim ama oyun yazma konusunda deneyimim yoktu. Araştırma yapmak için biraz zaman harcadım dev kalıpları verdim ve bu gözüme çarptı. Hiçbir şey yapmanın tek yolu bu değil, ama bunu çok sezgisel ve sağlam buldum. Desenin resmi olarak "Oyun Programlama Taşları" dizisinin 6. kitabında tartışıldığına inanıyorum - http://www.amazon.com/Game-Programming-Gems/dp/1584500492 . Hiçbir kitabı kendim okumadım ama oyun programlama için fiili referans olduklarını duydum.


1

Oyunları programlarken izlemeniz gereken açık kurallar yoktur. Oyunun her yerinde aynı tasarım desenini takip ettiğiniz sürece her iki deseni de mükemmel kabul edilebilir buluyorum. Birçoğu OOP'un üstünde bile olmayan oyunlar için daha fazla tasarım deseni olduğuna şaşıracaksınız.

Şimdi, kişisel tercihime göre, minimalist nesneler varken kodu davranışa göre ayırmayı tercih ediyorum (çizmek için bir sınıf / iş parçacığı, başka bir güncelleme için). Paralel hale getirmeyi daha kolay buluyorum ve genellikle kendimi aynı anda daha az dosya üzerinde çalışırken buluyorum. Bunun bir şeyler yapmanın daha usulsel bir yolu olduğunu söyleyebilirim.

Ancak iş dünyasından geliyorsanız, her şeyi kendileri için nasıl yapacağını bilen ders yazma konusunda muhtemelen daha rahatsınız.

Bir kez daha, sizin için en doğal olduğunu düşündüğünüzü yazmanızı tavsiye ederim.


Sanırım sorumu yanlış anladın. Her şeyi kendileri yapan sınıflara sahip olmaktan hoşlanmıyorum diyorum. Ben ... girdi vb taşıma, ancak oyun döngü görmemesi gereken, sadece bir top mantıksal gösterimi olması gereken bir top gibi düştü
BFree

Adil olmak gerekirse, iş programlamasında kaynatırsanız, iki tren vardır: kendilerine yük, devam eden ve mantık yükleyen iş nesneleri ve DTO + AppLogic + Depo / Persitance Katmanı ...
Nate

1

'Ball' nesnesinin nitelikleri ve davranışları vardır. Nesnenin kendine özgü niteliklerini ve davranışlarını kendi sınıflarına dahil etmenin mantıklı olduğunu düşünüyorum. Bir Ball nesnesinin güncelleme şekli, bir raketin güncelleme şeklinden farklıdır, bu nedenle bunların sınıfın dahil edilmesini gerektiren benzersiz davranışlar olduğu mantıklıdır. Çizim ile aynı. Bir raketin bir Top'a karşı çizilme biçiminde genellikle benzersiz bir fark vardır ve bu ihtiyaçlara uyacak şekilde bireysel bir nesnenin çizim yöntemini, bunları çözmek için başka bir sınıfta koşullu değerlendirmeler yapmaktan daha kolay değiştiririm.

Pong, nesne ve davranış sayısı açısından nispeten basit bir oyundur, ancak düzinelerce veya yüzlerce benzersiz oyun nesnesine girdiğinizde, 2. örneğinizi her şeyi modüle etmek için biraz daha kolay bulabilirsiniz.

Çoğu kişi, sonuçları bir Hizmet veya statik sınıf aracılığıyla tüm oyun nesneleri için kullanılabilen bir giriş sınıfı kullanır.


0

Bence iş dünyasında muhtemelen kullandığınız şey soyutlama ile uygulamayı birbirinden ayırmaktır.

Game dev dünyasında, genellikle hız açısından düşünmek istersiniz. Daha fazla nesneye sahip olduğunuzda, mevcut yüke bağlı olarak işleri yavaşlatabilecek daha fazla bağlam anahtarı ortaya çıkar.

Ben sadece bir spekülasyon çünkü bir özenti oyun geliştiricisi değil, profesyonel bir geliştirici.


0

Hayır, 'doğru' veya 'yanlış' değil, sadece basitleştirme.

Sunu ve mantığı zorla ayıran bir sistemle çalışmadığınız sürece bazı işletme kodları tamamen aynıdır.

Burada, bir GUI olay işleyicisine "iş mantığı" nın (sayı ekleyerek) yazıldığı bir VB.NET örneği - http://www.homeandlearn.co.uk/net/nets1p12.html

Her bir bileşen her şeyi kendileri yaparsa, çeşitli bileşenlerin karmaşıklığıyla nasıl başa çıkabilirim?

Bu kompleksi alırsa ve ne zaman alırsa, o zaman çarpanlarına ayırabilirsiniz.

class Ball
{
    GraphicalModel ballVisual;
    void Draw()
    {
        ballVisual.Draw();
    }
};

0

Geliştirme tecrübemden, çalıştığınız grupta kabul edilmiş bir uygulama olmadığı sürece, bir şeyler yapmanın doğru veya yanlış yolu yoktur. Davranışları, derileri vb. Ayırmaktan kaçınan geliştiricilerden muhalefetle karşılaştım. Ve eminim ki güneş altında her şeyi içeren bir sınıf yazmaya rezervasyonum hakkında da aynı şeyi söyleyecekler. Sadece yazmakla kalmayıp, yarın ve gelecekteki bir tarihte kodunuzu okuyabilmeniz, hata ayıklamanız ve büyük olasılıkla bir sonraki yinelemede bunun üzerine kurmanız gerektiğini unutmayın. Sizin (ve ekibiniz) için uygun bir yol seçmelisiniz. Başkalarının kullandığı bir rota seçmek (ve size karşı sezgisel) sizi yavaşlatacaktır.

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.