Tamamlanması için birden fazla kare alan oyun eylemleri


20

Daha önce hiç bu kadar çok programlama yapmadım, oldukça basit bir soru.

Ana döngü böyle bir şeye benzeyen bir Tetris oyunu inşa ettiğimi düşünün.

for every frame
    handle input
    if it's time to make the current block move down a row
        if we can move the block
            move the block
        else
            remove all complete rows
            move rows down so there are no gaps
            if we can spawn a new block
                spawn a new current block
            else
                game over

Oyunda her şey bugüne kadar anında olur - işler anında kökenli, ben eğer satırlar anında vb Ama ne kaldırılır yok şeyler anında (yani animate şeyler) olmasını istiyorum?

for every frame
    handle input
    if it's time to make the current block move down a row
        if we can move the block
            move the block
        else
            ?? animate complete rows disappearing (somehow, wait over multiple frames until the animation is done)
            ?? animate rows moving downwards (and again, wait over multiple frames)
            if we can spawn a new block
                spawn a new current block
            else
                game over

Pong klonumda bu bir sorun değildi, çünkü her kare sadece topu hareket ettiriyordum ve çarpışmaları kontrol ediyordum.

Kafamı bu sorunun etrafına nasıl sarabilirim? Elbette çoğu oyun bir çerçeveden daha fazlasını gerektiren bir eylem içerir ve diğer şeyler eylem tamamlanana kadar durur.

Yanıtlar:


11

Bunun geleneksel çözümü, birkaç yorumda önerilen sonlu durum makinesidir.

Sonlu durum makinelerinden nefret ediyorum.

Tabii, basitler, her dilde destekleniyorlar, ama birlikte çalışmak çok şaşırtıcı. Her manipülasyon bir ton bugprone kopyala-yapıştır kodu alır ve efekti küçük yollarla değiştirmek kodda büyük bir değişiklik olabilir.

Onları destekleyen bir dil kullanabiliyorsanız, yardımcı programlar öneririm. Şuna benzeyen bir kod yazmanıza izin verir:

function TetrisPieceExplosion()
  for brightness = 0, 1, 0.2 do
    SetExplosionBrightness(brightness)
    coroutine.yield()
  end

  AllowNewBlockToFall()

  SpawnABunchOfParticles()

  RemoveBlockPhysics()

  for transparency = 0, 1, 0.5 do
    SetBlockTransparency(transparency)
    coroutine.yield()
  end

  RemoveBlockGraphics()
end

Açıkçası oldukça pseudocodey, ancak bu özel efektin basit bir doğrusal açıklaması değil, aynı zamanda animasyon hala biterken yeni bir bloğu kolayca bırakmamıza izin veriyor . Bunu bir devlet makinesiyle başarmak genellikle korkunç olacaktır.

Bildiğim kadarıyla, bu işlevsellik C, C ++, C #, Objective C veya Java'da kolayca kullanılamaz. Bu, tüm oyun mantığım için Lua'yı kullanmamın ana nedenlerinden biri :)


Diğer OOP dillerinde de bu satırlar boyunca bir şeyler uygulayabilirsiniz. Bir çeşit Actionsınıf ve gerçekleştirilecek bir eylem sırası düşünün . Bir eylem tamamlandığında, işlemi kuyruktan kaldırın ve sonraki eylemi gerçekleştirin. Bir durum makinesinden çok daha esnek.
bummzack

3
Bu işe yarar, ama sonra her bir benzersiz eylem için Eylem'den türetmeye bakıyorsunuz. Ayrıca, işleminizin bir kuyruğa iyi uyduğunu varsayar - tanımlanmamış son koşullara sahip dallanma veya döngüler istiyorsanız, kuyruk çözümü hızlı bir şekilde parçalanır. Kesinlikle devlet makinesi yaklaşımından daha temiz, ama bence coroutines hala okunabilirlik üzerinde koz :)
ZorbaTHut

Doğru, ama Tetris örneği için yeterli olmalı :)
bummzack

Ortak rutinler rock- ama Lua bir dil olarak çok daha fazla şekilde berbat, sadece tavsiye edemem.
DeadMG

Sadece en üst düzeyde vermeniz gerektiği sürece (iç içe fonksiyon çağrısından değil), C # 'da aynı şeyi başarabilirsiniz, ancak evet, Lua coroutines rock.
munificent

8

Bunu Mike McShaffry'nin Game Coding Complete'ten alıyorum.

Yapılması gereken görevler listesine dayanan bir 'Süreç Yöneticisi' hakkında konuşuyor. Örneğin, bir işlem bir kılıç (AnimProcess) çizmek veya bir kapıyı açmak için animasyonu kontrol eder veya sizin durumunuzda satır kaybolur.

İşlem, süreç yöneticisinin listesine eklenecek ve her çerçeve yinelenecek ve her biri için Update () eklenecektir. Çok benzer varlıklar, ama eylemler için. Tamamlandığında listeden kaldırılacak bir öldürme bayrağı olurdu.

Onlarla ilgili diğer temiz şey, bir sonraki sürece bir işaretçi ile nasıl bağlantı kurabilecekleri. Bu şekilde, animasyon satırı işleminiz aslında aşağıdakilerden oluşabilir:

  • Kaybolan satır için bir AnimationProcess
  • Parçaları kaldırma işlemi
  • Puana puan eklemek için bir Puan Süreci

(Süreçler tek kullanımlık şeyler olabileceğinden, koşullu olarak orada veya X süre boyunca orada olabilir)

Daha fazla ayrıntı istiyorsanız, sorun.


3

Öncelikli bir işlem kuyruğu kullanabilirsiniz. Bir eylemi ve zamanı itersiniz. Her karede, zamanı alırsınız ve o zamandan önce belirtilmiş bir zamanı olan tüm eylemleri kapatır ve yürütürsünüz. Bonus: Yaklaşım güzel bir şekilde paraleldir ve aslında neredeyse tüm oyun mantığını bu şekilde uygulayabilirsiniz.


1

Her zaman önceki ve mevcut kare arasındaki zaman farkını bilmeniz gerekir, o zaman iki şey yapmanız gerekir.

-Modelinizi ne zaman güncelleyeceğinize karar verin: örn. Bir satır kaldırma işlemi başladığında tetris'te artık satırla bir şeyler çarpışmasını istemezsiniz, böylece satırı uygulamanızın 'modelinden' kaldırırsınız.

-Daha sonra geçiş halindeki nesneyi, animasyon / olayı belirli bir süre boyunca çözümleyen ayrı bir sınıfa işlemeniz gerekir. Tetris örneğinde, satır yavaş yavaş kaybolur (opaklığı her kareyi biraz değiştirin). Opaklık 0 olduktan sonra sıranın üstündeki tüm blokları bir aşağı aktarırsınız.

Bu başlangıçta biraz karmaşık görünebilir, ancak bunun asılmasını sağlayacaksınız, sadece farklı sınıflarda çok fazla soyut yaptığınızdan emin olun, bu daha kolay olacaktır. Ayrıca, tetris'deki bir satırın kaldırılması gibi zaman alan olayların "Ateşle ve Unut" türünde olduğundan, otomatik olarak yapılması gereken her şeyi işleyen yeni bir nesne oluşturduğundan ve her şey yapıldığında, kendini sahne grafiğinizden kaldırır.


Ayrıca, bazı durumlarda, ağır hesaplamalar bir fizik zaman adımı için izin verilen süreyi aşabilir (örn. Çarpışma algılama ve yol planlama). Bu durumlarda, ayrılan süre kullanıldığında hesaplamalardan atlayabilir ve hesaplamaya sonraki kareye devam edebilirsiniz.
Nailer

0

Oyunu bir "sonlu durum makinesi" olarak düşünmelisiniz. Oyun birkaç durumdan birinde olabilir: sizin durumunuzda, "giriş bekliyor", "parça aşağı hareket", "satır patlayan".

Devlete bağlı olarak farklı şeyler yaparsınız. Örneğin, "parça aşağı hareket ederken" oyuncu girdisini yok sayar ve bunun yerine parçayı geçerli satırdan sonraki satıra hareketlendirirsiniz. Bunun gibi bir şey:

if state == ACCEPTING_INPUT:
    if player presses any key:
        handle input
    row_timer = row_timer - time_since_last_frame
    if row_timer < 0:
        state = MOVING_PIECE_DOWN
elif state == MOVING_PIECE_DOWN:
    piece.y = piece.y + piece.speed*time_since_last_frame
    if piece.y >= target_piece_y:
        piece.y = target_piece_y
        state = ACCEPTING_INPUT
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.