Sıra tabanlı bir oyunda dünya devletinin ve animasyonun ayrılması


9

Sıra tabanlı bir oyunda animasyonun dünya devletinden ayrılmasını nasıl ele alıyorsunuz? Şu anda 2B ızgara tabanlı bir oyun üzerinde çalışıyorum. Aşağıdaki kod daha iyi açıklamak için basitleştirilmiştir.

Bir aktör hareket ettiğinde, yaratık canlanırken ve yeni konuma taşınırken dönüş akışını duraklatmak istiyorum. Aksi takdirde, ekran dünya durumunun önemli ölçüde gerisinde kalabilir ve bu da garip görsel görünüme neden olabilir. Ayrıca oyunun akışını engellemeyen animasyonlara sahip olmak istiyorum - bir parçacık efekti, oyunu etkilemeden çoklu dönüşlerde ortaya çıkabilir.

Yaptığım şey, engelleme animasyonları ve engellemeyen animasyonlar olarak adlandırdığım iki tür animasyon tanıtmak. Oyuncu hareket etmek istediğinde, yürütülen kod

class Actor {
    void move(PositionInfo oldPosition, PositionInfo newPosition) {
        if(isValidMove(oldPosition, newPosition) {
             getGame().pushBlockingAnimation(new MoveAnimation(this, oldPosition, newPosition, ....));
             player.setPosition(newPosition);
        }
    }
}

Ardından ana güncelleme döngüsü şunları yapar:

class Game {
    void update(float dt) {
        updateNonBlockingAnimations(dt); //Effects like particle explosions, damage numbers, etc. Things that don't block the flow of turns.
        if(!mBlockingAnimations.empty()) {
            mBlockingAnimations.get(0).update(dt);
        } else {
             //.. handle the next turn. This is where new animations will be enqueued..//
        }
        cleanUpAnimations(); // remove finished animations
    }
}

... burada animasyon Aktörün ekran konumunu günceller.

Uyguladığım diğer bir fikir, birden fazla engelleme animasyonunun aynı anda güncelleneceği eşzamanlı bir engelleme animasyonuna sahip olmaktır, ancak bir sonraki dönüş hepsi tamamlanana kadar gerçekleşmez.

Bu bir şeyler yapmanın akılcı bir yolu gibi mi görünüyor? Herkes herhangi bir öneri, hatta diğer benzer oyunların böyle bir şey yapmak için referanslar var mı.

Yanıtlar:


2

Sisteminiz sizin için çalışıyorsa, bunu yapmamak için herhangi bir neden göremiyorum.

Bir sebep: "player.setPosition (newPosition);" sonuçlarını kolayca yargılayamazsanız dağınık olabilir. Artık.

Örnek (sadece işleri uyduruyor): Player'ı setPosition ile bir tuzağın üstüne taşıdığınızı düşünün. Kendisine "player.setPosition" çağrısı, başka bir çağrıyı tetikler .. bir tuzak dinleyici kodu söyle, bu da kendi başına "acı veren bir sıçrama" görüntüleyen engelleme animasyonu başlatacaktır.

Sorunu görüyorsunuz: sıçrama , oyuncunun tuzağa doğru hareket etmesiyle eşzamanlı olarak görüntüleniyor . (Burada bunu istemediğinizi varsayalım, ancak taşıma bittikten hemen sonra tuzak animasyonunun oynatılmasını istiyorum;)).

Bu nedenle, bu "pushBlockingAnimation" çağrılarından sonra yaptığınız eylemlerinizin tüm sonuçlarını aklınızda tutabildiğiniz sürece, her şey yolunda demektir.

Animasyon bittikten sonra yürütmek için sıraya alınabilmeleri için oyun mantığını "bir şeyi engelleme" sınıflarına sarmaya başlamanız gerekebilir. Veya pushBlockingAnimation öğesine bir callback "callThisAfterAnimationFinishes" iletir ve setPosition öğesini geri arama işlevine taşırsınız.

Başka bir yaklaşım, dil senaryoları olacaktır. Örneğin Lua, işlevi özel bir durumla döndüren "coroutine.yield" e sahiptir, böylece bir sonraki ifadede devam etmek için daha sonra çağrılabilir. Bu şekilde, kodunuzun "normal" program akışı görünümünü karıştırmadan animasyonun sonunun oyun mantığını yürütmesini kolayca bekleyebilirsiniz. (kodunuzun, geri çağrıları iletmek ve oyun mantığını birden fazla işlev üzerinde karıştırmak yerine yine de "playAnimation (); setPosition ();" gibi görüneceği anlamına gelir).

Unity3D (ve bildiğim en az bir diğer oyun sistemi) bunu doğrudan C # kodunda simüle etmek için C # "geri dönüş" ifadesini içerir.

// instead of simple call to move(), you have to "iterate" it in a foreach-like loop
IEnumerable<Updatable> move(...) {
    // playAnimation returns an object that contain an update() and finished() method.
    // the caller will not iterate over move() again until the object is "finished"
    yield return getGame().playAnimation(...)
    // the next statement will be executed *after* the animation finished
    player.setPosition(newPosition);
}

Tabii ki, bu şemada move () çağrısı şimdi tüm kirli işleri alır. Bu tekniği muhtemelen onları tam olarak kimin nereden çağırdığını bildiğiniz belirli işlevler için uygularsınız. Öyleyse bunu sadece diğer motorların nasıl düzenlendiği hakkında temel bir fikir olarak kabul edin - muhtemelen mevcut özel probleminiz için çok karmaşık.

Gerçek dünya probleminiz olana kadar pushBlockingAnimation fikrinize bağlı kalmanızı tavsiye ederim. Sonra değiştirin. ;)


Bu yanıtı yazmak için zaman ayırdığınız için çok teşekkür ederim, gerçekten minnettarım.
mdkess
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.