Nesneleri oluşturma işleminden neden ayırmalıyım?


11

Disclamer: Ben bir varlık sistemi desen ne olduğunu ve ben değilim değil kullanmaktan.

Nesneyi ayırma ve oluşturma hakkında çok şey okudum. Oyun mantığının altta yatan oluşturma motorundan bağımsız olması gerektiği hakkında. Hepsi iyi ve züppe ve mükemmel bir anlam ifade ediyor, ancak başka birçok acıya da neden oluyor:

  • mantık nesnesi ve oluşturma nesnesi (animasyonun durumunu, sprite'ları vb. tutan) senkronizasyon ihtiyacı
  • oluşturma nesnesinin mantık nesnesinin gerçek durumunu okuması için mantık nesnesini herkese açmanız gerekir (genellikle mantık nesnesinin aptal bir alıcı ve ayarlayıcı nesnede kolayca dönüşmesine neden olur)

Bu bana iyi bir çözüm gibi gelmiyor. Öte yandan, bir nesneyi 3d (veya 2d) temsili olarak hayal etmek çok sezgiseldir ve aynı zamanda bakımı çok kolaydır (ve muhtemelen daha da kapsüllenmiştir).

Grafik sunumunu ve oyun mantığını bir araya getirmenin (senkronizasyon sorunlarından kaçınmanın), ancak oluşturma motorunu soyutlamanın bir yolu var mı? Veya yukarıdaki dezavantajlara neden olmayan oyun mantığını ve oluşturmayı ayırmanın bir yolu var mı?

(muhtemelen örneklerle, soyut konuşmaları anlamada çok iyi değilim)


1
Varlık sistemi modelini kullanmadığınızı söylediğinizde ne demek istediğinizi ve bunun, oluşturma endişesini varlık / oyun mantığı.
michael.bartnett

@ michael.bartnett, desenlerin çoğu uygulamasının yaptığı gibi, sistemler tarafından işlenen küçük yeniden kullanılabilir bileşenlerdeki nesneleri ayırmıyorum. Benim kod yerine MVC desen daha bir girişimdir. Ancak, soru herhangi bir koda (hatta bir dile bile) bağlı olmadığı için gerçekten önemli değil. Feragatnameyi koydum çünkü bazılarının beni ECS'yi kullanmaya ikna etmeye çalışacağını biliyordum, bu da kanseri tedavi ediyor gibi görünüyor. Ve gördüğünüz gibi, yine de oldu.
Ayakkabı

Yanıtlar:


13

Diyelim ki bir dünya , bir oyuncu ve bir patrondan oluşan bir sahneniz var . Oh, ve bu üçüncü şahıs oyunu, yani bir kameran da var .

Yani sahneniz şöyle:

class Scene {
    World* world
    Player* player
    Enemy* boss
    Camera* camera
}

(En azından bu temel verilerdir . Verileri nasıl içerdiğiniz size bağlıdır.)

Sahneyi yalnızca oyunu oynarken, duraklatıldığında veya ana menüde güncellemek ve işlemek istersiniz ... böylece oyun durumuna eklersiniz!

State* gameState = new State();
gameState->addScene(scene);

Şimdi oyun durumunuzun bir sahnesi var. Ardından, mantığı sahnede çalıştırmak ve sahneyi oluşturmak istiyorsunuz. Mantık için, sadece bir güncelleme işlevi çalıştırırsınız.

State::update(double delta) {
    scene->update(delta);
}

Bu şekilde tüm oyun mantığını Scenesınıfta tutabilirsiniz . Ve sadece referans amacıyla, bir varlık bileşen sistemi bunu şu şekilde yapabilir:

State::update(double delta) {
    physicsSystem->applyPhysics(scene);
}

Her neyse, artık sahnenizi güncellemeyi başardınız. Şimdi görüntülemek istiyorsunuz! Bunun için yukarıdakine benzer bir şey yapıyoruz:

State::render() {
    renderSystem->render(scene);
}

İşte böyle. RenderSystem olay yerindeki bilgileri okur ve uygun görüntüyü gösterir. Basitleştirilmiş, sahneyi oluşturma yöntemi şöyle görünebilir:

RenderSystem::renderScene(Scene* scene) {
    Camera* camera = scene->camera;
    lookAt(camera); // Set up the appropriate viewing matrices based on 
                    // the camera location and direction

    renderHeightmap(scene->getWorld()->getHeightMap()); // Just as an example, you might
                                                        // use a height map as your world
                                                        // representation.
    renderModel(scene->getPlayer()->getType()); // getType() will return, for example "orc"
                                                // or "human"

    renderModel(scene->getBoss()->getType());
}

Gerçekten basitleştirilmiş olsa da, örneğin, oynatıcınızın nerede olduğuna ve nereye baktığına bağlı olarak bir rotasyon ve çeviri uygulamanız gerekir. (Benim örneğim bir 3D oyun, 2D ile giderseniz, parkta bir yürüyüş olacak).

Umarım aradığın şey budur? Umarım yukarıdakilerden hatırlayabileceğiniz gibi, render sistemi oyunun mantığını umursamaz . Oluşturmak için yalnızca sahnenin geçerli durumunu kullanır, yani oluşturmak için gerekli bilgileri çeker. Ya oyun mantığı? Oluşturucunun ne yaptığı önemli değil. Heck, gösterilmesinin hiç umrunda değil!

Ayrıca sahneye oluşturma bilgileri eklemenize de gerek yoktur. Oluşturucunun bir ork oluşturması gerektiğini bilmesi yeterli olmalıdır. Oluşturucunun görüntülemesini bildiği bir ork modeli zaten yüklediniz.

Bu, gereksinimlerinizi karşılamalıdır. Grafik gösterimi ve mantık birleştirilir , çünkü ikisi de aynı verileri kullanır. Yine de ayrılar , çünkü ikisi de diğerine dayanmıyor!

EDIT: Ve sadece neden böyle yapmak için cevap ? Çünkü daha kolay, en basit nedendir. "Böyle ve böyle oldu, şimdi grafikleri güncellemeliyim" diye düşünmenize gerek yok. Bunun yerine bir şeyler yaparsınız ve oyunun her karesi şu anda olanlara bakar ve bir şekilde yorumlar ve size bir ekran sonucu verir.


7

Başlığınız, vücut içeriğinizden farklı bir soru soruyor. Başlıkta, mantık ve oluşturmanın neden ayrılması gerektiğini sorarsınız, ancak gövdede mantık / grafik / oluşturma sistemleri uygulamalarını istersiniz.

İkinci soru daha önce ele alınmıştı , bu yüzden ilk soruya odaklanacağım.

Mantığı ayırmak ve oluşturmak için nedenler:

  1. Nesnelerin bir şey yapması gerektiği konusunda yaygın olarak kabul gören
  2. 2B'den 3B'ye gitmek isterseniz ne olur? Projenin ortasında bir oluşturma sisteminden diğerine geçmeye karar verirseniz ne olur? Tüm kodunuzu taramak ve oyun mantığınızın ortasında büyük değişiklikler yapmak istemezsiniz.
  3. Genelde kötü bir fikir olarak kabul edilen kod bölümlerini tekrarlamak için muhtemelen nedeniniz olacaktır .
  4. Küçük parçalarla tek tek iletişim kurmadan potansiyel olarak büyük oluşturma veya mantık yığınlarını kontrol etmek için sistemler oluşturabilirsiniz.
  5. Bir oyuncuya bir taş atamak istiyorsanız, ancak sistem taşın kaç yönü olduğundan yavaşlarsa? Oluşturma sisteminizi yeterince iyi soyutladıysanız, pahalı oluşturma işlemlerini hesaba katmak için farklı hızlarda güncelleyebilirsiniz.
  6. Yaptıklarınız için gerçekten önemli olan şeyleri düşünmenizi sağlar. Neden tek yapmak istediğiniz bir çift atlama mekaniği uygulamak, bir kart çekmek veya bir kılıç donatmak olduğunda beyninizi matris dönüşümleri etrafında döndürür ve ofsetleri ve ekran koordinatlarını döndürür? Sadece sağ elinizden sola doğru hareket ettirmek istediğiniz için donanımlı kılıcınızı temsil eden hareketli grafiğin parlak pembe renkte olmasını istemezsiniz.

Bir OOP ayarında yeni nesnelerin başlatılmasının bir maliyeti vardır, ancak benim deneyimime göre, sistem kaynaklarına maliyeti, yapılması gereken belirli şeyleri düşünme ve uygulama yeteneği için ödenecek küçük bir fiyattır.


6

Bu cevap, doğrudan pratik örnekler önermekten ziyade, render ve mantığın ayrılmasının neden önemli olduğunun sezgisini oluşturmaktır.

Büyük bir filimiz olduğunu varsayalım , odada kimse filin tamamını göremiyor. belki herkes gerçekte ne olduğu konusunda hemfikir değil. Çünkü herkes filin farklı bir parçasını görür ve sadece bu parçayla başa çıkabilir. Ama sonunda bu büyük bir fil olduğu gerçeğini değiştirmez.

Fil oyun nesnesini tüm detayları ile temsil eder. Ama aslında işlevselliğini yapabilmek için kimsenin fil (oyun nesnesi) hakkında her şeyi bilmesine gerek yok.

Oyun mantığını ve oluşturmayı birleştirmek aslında herkesin tüm fili görmesini sağlamak gibidir. Bir şey değiştiğinde herkesin bilmesi gerekir. Çoğu durumda sadece ilgilendikleri kısmı görmeleri gerekir. Eğer bir şey onu bilen kişiyi değiştirirse, diğer kişiye sadece bu değişimin sonucu hakkında bilgi vermesi gerekir, bu onun için önemli olan şeydir. (bunu mesajlar veya arayüzler yoluyla iletişim olarak düşünün).

resim açıklamasını buraya girin

Bahsettiğiniz noktalar geri çekilmiyor, sadece motorda olması gerekenden daha fazla bağımlılık varsa geri çekiliyor, yani sistemler filin parçalarını olması gerekenden daha fazla görüyor. Bu da motorun "doğru şekilde" tasarlanmadığı anlamına gelir.

Mantık ve oluşturmayı iki farklı iş parçacığına yerleştirdiği çok iş parçacıklı bir motor kullanıyorsanız ve biçimsel tanımlamayla senkronizasyona ihtiyacınız vardır ve hatta sistemler arasında çok fazla senkronizasyon gerektiren bir motor bile çok fazla tasarlanmamıştır.

Aksi takdirde, bu durumla başa çıkmanın doğal yolu, sistemi giriş / çıkış olarak tasarlamaktır. Güncelleme mantığı yapar ve sonucu verir. Oluşturma yalnızca güncellemenin sonuçlarıyla beslenir. Gerçekten her şeyi açığa vurmanıza gerek yok. Yalnızca iki aşama arasında iletişim kuran bir Arabirimi açığa çıkarırsınız. Motorun farklı parçaları arasındaki iletişim soyutlamalar (arayüzler) ve / veya mesajlar yoluyla yapılmalıdır. Hiçbir iç mantık veya durum açıklanmamalıdır.

Fikri açıklamak için basit bir sahne grafiği örneği alalım.

Güncelleme genellikle oyun döngüsü adı verilen tek bir döngü (veya muhtemelen her biri ayrı bir iş parçacığında çalışan) birden çok oyun döngüsü aracılığıyla yapılır. Bir kez döngü hiç oyun nesnesi güncellendi. Sadece mesajlaşma veya arayüzler aracılığıyla güncellenen 1 ve 2 nesnelerinin son dönüşümle beslenmesi ve beslenmesi gerekir.

Oluşturma sistemi yalnızca son dönüşümü alır ve nesne hakkında gerçekte ne değiştiğini bilmez (örneğin, özel çarpışma oldu vb.). Şimdi bu nesneyi oluşturmak için sadece o nesnenin kimliğine ve son dönüşüme ihtiyacı var. Bundan sonra, oluşturucu render api'sini başka bir şey bilmeden ağ ve son dönüşümle besleyecektir.

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.