Bir hiyerarşiyi zorlamadan birbirleriyle etkileşimde bulunan ve iletişim kuran nesneleri nasıl alabilirim?


9

Umarım bu haydutlar sorumu açıklığa kavuşturacaklardır - yine de alışkanlık olup olmadıklarını tamamen anlarım, bu yüzden durumun bu olup olmadığını bana bildirin ve kendimi daha net hale getirmeye çalışacağım.

Nesne yönelimli oyun geliştirme ile tanışmak için yaptığım çok basit bir oyun olan BoxPong ile tanışın . Topu kontrol etmek ve sarı şeyler toplamak için kutuyu sürükleyin.
BoxPong yapmak, diğer şeylerin yanı sıra temel bir soru formüle etmeme yardımcı oldu: Birbirlerine "ait olmak" zorunda kalmadan birbirleriyle etkileşen nesneler nasıl olabilir? Başka bir deyişle, nesnelerin hiyerarşik değil, bir arada var olmalarının bir yolu var mı? (Aşağıda daha ayrıntılı olarak açıklayacağım.)

Var olan nesneler sorununun ortak bir sorun olduğundan şüpheleniyorum, umarım bunu çözmek için yerleşik bir yol vardır. Kare tekerleği yeniden icat etmek istemiyorum, bu yüzden aradığım ideal cevap "işte probleminizi çözmek için yaygın olarak kullanılan bir tasarım deseni" dir.

Özellikle BoxPong gibi basit oyunlarda, aynı seviyede var olan bir avuç nesne olduğu veya olması gerektiği açıktır. Bir kutu var, bir top var, bir koleksiyon var. Nesneye yönelik dillerde ifade edebileceğim tek şey - ya da öyle görünüyor ki - katı HAS-A ilişkileri. Bu üye değişkenler aracılığıyla yapılır. Sadece başlayamıyorum ballve onun işini yapmasına izin veremem , kalıcı olarak başka bir nesneye ait olması gerekiyor . Öyle ana oyun nesne o kadar kurdum sahip bir kutu ve bunun sonucunda da kutu vardır bir topu ve sahip bir skor sayacı. Her nesnenin ayrıca birupdate()Ben tüm çocukların güncelleme yöntemlerini çağırır ana oyun nesnenin güncelleme yöntemini çağırmak ve onlar da tüm güncelleme yöntemleri çağırmak: vb pozisyon, yön hesaplar ve orada da benzer bir yol yöntem, onların çocukları. Nesneye yönelik bir oyun yapmanın tek yolu bu, ama bunun ideal yol olmadığını hissediyorum. Sonuçta, topu tam olarak kutuya ait olarak düşünmüyorum, aksine aynı seviyede olmak ve onunla etkileşime girmek olarak düşünürdüm. Bunun, tüm oyun nesnelerini ana oyun nesnesinin üye değişkenlerine dönüştürerek elde edilebileceğini düşünüyorum, ancak bunu hiçbir şeyin çözdüğünü görmüyorum. Yani ... bariz karmaşayı bir kenara bırakarak, topun ve kutunun birbirini tanıması , yani etkileşmesi için nasıl bir yol olurdu ?

Birbirlerinden bilgi aktarması gereken nesneler de var. Neredeyse tüm RAM'e her zaman erişebileceğiniz SNES için kod yazma konusunda biraz deneyimim var. Süper Mario Dünyası için özel bir düşman oluşturduğunuzu ve Mario'nun tüm madeni paralarını kaldırmasını istediğinizi varsayalım, o zaman sıfır $ 0DBF adresinde saklayın, sorun değil. Düşmanların oyuncunun durumuna erişemediğini söyleyen bir sınırlama yoktur. Sanırım bu özgürlükten şımarık oldum, çünkü C ++ ve benzerleri ile sık sık kendimi başka bir nesneye (hatta küresel) erişilebilen bir değer haline getirmeyi merak ediyorum.
BoxPong örneğini kullanarak, topun ekranın kenarlarından sekmesini istersem ne olur? widthve sınıfın heightözellikleridir Game,ballbunlara erişebilmek için. Bu tür değerleri aktarabilirim (ya yapıcılar ya da ihtiyaç duydukları yöntemler aracılığıyla), ama bu sadece bana kötü uygulama çığlık atıyor.

Sanırım asıl sorun, birbirlerini tanımak için nesnelere ihtiyacım var, ama bunu yapabilmemin tek yolu çirkin ve pratik olmayan katı hiyerarşi.

C ++ 'da "arkadaş sınıfları" duydum ve nasıl çalıştığını biliyorum, ama eğer hepsi bir çözüm, o zaman nasıl friendher tek C ++ projesinin üzerine dökülen anahtar kelimeler görmüyorum ve nasıl kavramı her OOP dilinde mevcut değil mi? (Aynısı yakın zamanda öğrendiğim fonksiyon göstergeleri için de geçerli.)

Her türlü cevap için şimdiden teşekkürler - ve yine, size mantıklı olmayan bir parça varsa, bana bildirin.


2
Oyun endüstrisinin çoğu Entity-Component-System mimarisine ve varyasyonlarına doğru ilerledi. Geleneksel OO yaklaşımlarından farklı bir zihniyettir ama kavram işe yaradığında iyi çalışır ve mantıklıdır. Birlik bunu kullanır. Aslında, Unity yalnızca Entity-Component bölümünü kullanır ancak ECS'ye dayanır.
Dunk

Sınıfların birbirleri hakkında bilgi sahibi olmadan birbirleriyle işbirliği yapmalarına izin verme sorunu, Arabulucu tasarım modeli tarafından çözülmektedir. Ona baktın mı?
Fuhrmanator

Yanıtlar:


13

Genel olarak, aynı seviyedeki nesneler birbirini biliyorsa çok kötü çıkıyor. Nesneler birbirlerini tanıdıktan sonra bağlanırlar veya birbirlerine bağlanırlar . Bu onları değiştirmeyi, test etmeyi, bakımını zorlaştırır.

İkisini bilen ve aralarındaki etkileşimi ayarlayabilen "yukarıda" bir nesne varsa çok daha iyi sonuç verir. İki akranı bilen nesne, bağımlılık enjeksiyonu veya olaylar yoluyla ya da mesaj iletme (ya da herhangi bir ayırma mekanizması) yoluyla bunları birbirine bağlayabilir. Evet, bu biraz yapay bir hiyerarşiye yol açar, ancak işler willy-nilly ile etkileşime girdiğinde elde ettiğiniz spagetti karmaşasından çok daha iyidir. Bu sadece C ++ 'da daha önemlidir, çünkü nesnelerin ömrüne de sahip olacak bir şeye ihtiyacınız vardır.

Kısacası, nesneleri her yerde geçici erişim ile birbirine bağlayarak yapabilirsiniz, ancak bu kötü bir fikirdir. Hiyerarşi düzen ve açık sahiplik sağlar. Hatırlanması gereken en önemli şey, koddaki nesnelerin gerçek hayatta (hatta oyunda) mutlaka nesne olmamasıdır. Oyundaki nesneler iyi bir hiyerarşi oluşturmazsa, farklı bir soyutlama daha iyi olabilir.


2

BoxPong örneğini kullanarak, topun ekranın kenarlarından sekmesini istersem ne olur? genişlik ve yükseklik Oyun sınıfının özellikleridir ve onlara erişebilmem için topa ihtiyacım var.

Hayır!

Bence asıl mesele, "Nesneye Dayalı Programlama" yı biraz fazla anlamış olmanızdır. OOP'de bir nesne bir "şeyi" temsil etmez, "Top", "Oyun", "Fizik", "Matematik", "Tarih" vb. Anlamına gelen bir "fikri" temsil eder. Hepsi geçerli nesnelerdir. Nesnelerin herhangi bir şey hakkında "bilmesi" gerekmemektedir. Örneğin Date.Now().getTommorrow(), bilgisayara bugünün hangi gün olduğunu sorar mısın, yarının tarihini bulmak için gizli tarih kuralları uygulayın ve bunu arayan kişiye geri gönderin. DateSistemden gerektiği gibi başka bir şey, sadece talep bilgisine ihtiyacı hakkında nesne bilmiyor. Ayrıca, Math.SquareRoot(number)bir kare kökün nasıl hesaplanacağının mantığının yanı sıra hiçbir şey bilmesine gerek yoktur.

Bu yüzden alıntıladığım örnekte "Top", "Kutu" hakkında hiçbir şey bilmemelidir. Kutular ve Toplar tamamen farklı fikirlerdir ve birbirleriyle konuşma hakları yoktur. Ancak bir Fizik motoru, bir Kutu ve Topun ne olduğunu (veya en azından ThreeDShape) bilir ve nerede olduklarını ve onlara ne olması gerektiğini bilir. Eğer top soğuk olduğu için büzülürse, Fizik Motoru o top örneğine şimdi daha küçük olduğunu söylerdi.

Bir araba yapmak gibi. Bir bilgisayar çipi bir araba motoru hakkında hiçbir şey bilmez, ancak bir araba bir motoru kontrol etmek için bir bilgisayar çipi kullanabilir. Biraz daha büyük, daha karmaşık bir şey yaratmak için küçük, basit şeyleri bir arada kullanma fikri, diğer daha karmaşık parçaların bir bileşeni olarak tekrar kullanılabilir.

Ve Mario örneğinizde, bir düşmana dokunmanın Marios paralarını boşaltmadığı, ancak onu o odadan çıkardığı bir meydan okuma odasındaysanız ne olacak? Mario ya da düşmanın fikir alanının dışında, bir düşmana dokunduğunda Mario'nun para kaybetmesi gerekir (aslında, Mario'nun bir savunmasızlık yıldızı varsa, onun yerine düşmanı öldürür). Dolayısıyla, mario bir düşmana dokunduğunda gerçekleşen şeyden sorumlu olan nesne (etki alanı / fikir), ikisinden biri hakkında bilmesi gereken tek şeydir ve bunlardan herhangi birine ne yapması gerekiyorsa (nesnenin dış kaynaklı değişikliklere izin verdiği ölçüde) ).

Ayrıca, çocukları çağıran nesneler hakkındaki ifadelerinizle , farklı ebeveynlerden kare başına birden çok kez çağrılırsa bu Update()hataya son derece yatkındır Update? (bunu yakalasanız bile, oyununuzu yavaşlatabilecek CPU zamanını boşa harcar) Herkes sadece ihtiyaç duydukları şeylere ihtiyaç duyduklarında dokunmalıdır. Update () kullanıyorsanız, tüm Güncellemelerin çerçeve başına bir kez çağrıldığından emin olmak için bir tür abonelik modeli kullanmanız gerekir (bu Unity'de olduğu gibi işlenmezse)

Etki alanı fikirlerinizi net, izole, iyi tanımlanmış ve kullanımı kolay bloklara nasıl tanımlayacağınızı öğrenmek, OOP'yi ne kadar iyi kullanabileceğinizin en büyük faktörü olacaktır.


1

Nesne yönelimli oyun geliştirme ile tanışmak için yaptığım çok basit bir oyun olan BoxPong ile tanışın.

BoxPong yapmak, diğer şeylerin yanı sıra temel bir soru formüle etmeme yardımcı oldu: Birbirlerine "ait olmak" zorunda kalmadan birbirleriyle etkileşen nesneler nasıl olabilir?

Neredeyse tüm RAM'e her zaman erişebileceğiniz SNES için kod yazma konusunda biraz deneyimim var. Süper Mario Dünyası için özel bir düşman oluşturduğunuzu ve Mario'nun tüm madeni paralarını kaldırmasını istediğinizi varsayalım, sadece 0 $ 0DBF adresine ulaşmak için sıfır depolayın, sorun değil.

Nesne yönelimli programlama noktasını kaçırıyorsunuz.

Nesneye Yönelik Programlama, sağlamlığınızı, kırılganlığınızı ve yeniden kullanılamazlığı önleyebilmeniz için mimarinizdeki belirli anahtar bağımlılıkları seçici olarak tersine çevirerek bağımlılıkları yönetmekle ilgilidir.

Bağımlılık nedir? Bağımlılık başka bir şeye güvenmektir. $ 0DBF'ye adres vermek için sıfır depoladığınızda, bu adresin Mario'nun madeni paralarının bulunduğu yere ve madeni paraların bir tamsayı olarak temsil edildiğine güveniyorsunuz. Özel Düşman kodunuz Mario ve madeni paralarını uygulayan koda bağlıdır. Mario'nun madeni paralarını belleğe kaydettiği yerde bir değişiklik yaparsanız, bellek konumuna başvuran tüm kodu manuel olarak güncellemeniz gerekir.

Nesneye yönelik kod, kodunuzu ayrıntılara değil soyutlamalara bağımlı kılmakla ilgilidir. Yani yerine

class Mario
{
    public:
        int coins;
}

yazardın

class Mario
{
    public:
        void LoseCoins();

    private:
        int coins;
}

Şimdi, Mario'nun madeni paralarını bir int'ten uzun veya iki katına nasıl depoladığını değiştirmek veya ağda depolamak veya bir veritabanında saklamak veya başka bir uzun süreci başlatmak istiyorsanız, değişikliği tek bir yerde yaparsınız: Mario sınıfı ve diğer tüm kodlarınız hiçbir değişiklik olmadan çalışmaya devam ediyor.

Bu nedenle, sorduğunuzda

Birbirlerine "ait olmak" zorunda kalmadan birbirleriyle etkileşime giren nesneler nasıl olabilir?

gerçekten soruyorsun:

herhangi bir soyutlama olmadan doğrudan birbirine bağlı kodu nasıl alabilirim?

nesne yönelimli programlama değildir.

Burada her şeyi okuyarak başlamanızı öneririm: http://objectmentor.com/omSolutions/oops_what.html ve ardından Robert Martin tarafından her şeyi youtube'da arayın ve hepsini izleyin.

Cevaplarım ondan geliyor ve bir kısmı doğrudan ondan alıntılanıyor.


Cevabınız için teşekkürler (ve bağlantı verdiğiniz sayfa; ilginç görünüyor). Aslında soyutlama ve yeniden kullanılabilirlik hakkında bilgim var, ama sanırım cevabımı çok iyi bir şekilde ortaya koyamadım. Ancak, verdiğiniz örnek koddan şimdi daha iyi bir şekilde açıklayabilirim! Temel olarak düşman nesnesinin yapmaması gerektiğini söylüyorsunuz mario.coins = 0;, ama mario.loseCoins();bu iyi ve doğru - ama benim açımdan, düşmanın marionesneye nasıl erişimi olabilir ? marioüye değişkeni enemyolmak bana doğru gelmiyor.
vvye

Basit cevap, Mario'yu Düşman'daki bir işleve argüman olarak iletmektir. Mario'yu argüman olarak alacak marioNearby () veya attackMario () gibi bir fonksiyonunuz olabilir. Böylece, bir Düşman ve bir Mario'nun etkileşime girmesi gereken arkasındaki mantık tetiklendiğinde, mario.loseCoins () olarak adlandırılan enemy.marioNearby (mario) 'yı çağırırsınız; Daha sonra yolda mario'nun sadece bir jeton kaybetmesine ve hatta jeton kazanmasına neden olan bir düşman sınıfı olduğuna karar verebilirsiniz. Artık, diğer kodda yan etki değişikliklerine neden olmayan bu değişikliği yapmak için tek bir yeriniz var.
Mark Murfin

Mario'yu bir düşmana geçirerek onları birleştirdiniz. Mario ve Düşman ötekinin bir şey olduğunu bile bilmemelidir. Bu nedenle basit nesnelerin nasıl birleştirileceğini yönetmek için daha yüksek dereceli nesneler yaratırız.
Tezra

@Tezra Peki, bu üst düzey nesneler tekrar kullanılamaz mı? Bu nesneler fonksiyonlar gibi davranıyor, sadece sergiledikleri prosedür olarak varlar.
Steve Chamaillard

@SteveChamaillard Her programın, başka herhangi bir programda mantıklı olmayan en azından biraz özel mantığı olacaktır, ancak fikir, bu mantığı birkaç yüksek dereceli sınıfa izole tutmaktır. Eğer mario, düşman ve seviye sınıfın varsa mario ve düşmanı diğer oyunlarda tekrar kullanabilirsin. Düşman ve mario'yu doğrudan birbirine bağlarsanız, birinin ihtiyacı olan herhangi bir oyundan diğerini çekmesi gerekir.
Tezra

0

Aracı desenini uygulayarak gevşek bağlantıyı etkinleştirebilirsiniz. Bunu uygulamak, tüm alıcı bileşenleri bilen bir arabulucu bileşenine başvurmanızı gerektirir.

"Oyun ustası, lütfen buna izin verin ve böyle bir şey" düşüncesini fark eder.

Genelleştirilmiş bir desen, yayınlama-abone olma düzenidir. Arabulucunun fazla mantık içermemesi uygun olur. Aksi takdirde, tüm çağrıları nereye yönlendireceğini ve hatta hatta değiştirmeyi bilen el yapımı bir arabulucu kullanın.

Genellikle olay veri yolu, ileti veri yolu veya ileti sırası olarak adlandırılan eşzamanlı ve eşzamansız değişkenler vardır. Özel durumunuzda uygun olup olmadıklarını belirlemek için onlara bakın.

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.