Aksi game yang membutuhkan beberapa frame untuk diselesaikan

20

Saya belum pernah benar-benar melakukan banyak pemrograman game sebelumnya, pertanyaan yang cukup mudah.

Bayangkan saya membangun game Tetris, dengan loop utama terlihat seperti ini.

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

Semua yang ada dalam game sejauh ini terjadi secara instan - semuanya muncul secara instan, baris dihapus secara instan, dll. Tetapi bagaimana jika saya tidak ingin hal-hal terjadi secara instan (yaitu hal-hal yang bernyawa)?

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

Dalam klon Pong saya ini bukan masalah, karena setiap frame saya hanya menggerakkan bola dan memeriksa tabrakan.

Bagaimana saya bisa mengatasi masalah ini? Tentunya sebagian besar permainan melibatkan beberapa aksi yang membutuhkan lebih dari satu frame, dan hal-hal lain berhenti sampai aksi selesai.


sumber

Jawaban:

11

Solusi tradisional untuk ini adalah mesin keadaan terbatas, yang sedang disarankan dalam beberapa komentar.

Aku benci mesin negara yang terbatas.

Tentu, mereka sederhana, mereka didukung dalam setiap bahasa, tetapi mereka sangat menyebalkan. Setiap manipulasi membutuhkan satu ton kode copy-and-paste bugprone, dan mengubah efeknya dengan cara-cara kecil bisa menjadi perubahan besar pada kode.

Jika Anda dapat menggunakan bahasa yang mendukungnya, saya sarankan coroutine. Mereka membiarkan Anda menulis kode yang terlihat seperti:

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

Jelas agak pseudocodey, tetapi harus jelas bahwa ini bukan hanya deskripsi linear sederhana dari efek khusus, tetapi juga dengan mudah memungkinkan kita menjatuhkan blok baru saat animasi masih selesai . Menyelesaikan ini dengan mesin negara umumnya akan mengerikan.

Sejauh pengetahuan saya, fungsi ini tidak mudah tersedia di C, C ++, C #, Objective C, atau Java. Ini adalah salah satu alasan utama saya menggunakan Lua untuk semua logika permainan saya :)

ZorbaTHut
sumber
Anda juga bisa menerapkan sesuatu di sepanjang garis ini dalam bahasa OOP lainnya. Bayangkan semacam Actionkelas dan antrian tindakan untuk dilakukan. Ketika suatu tindakan selesai, hapus dari antrian dan lakukan tindakan selanjutnya dll. Jauh lebih fleksibel daripada mesin-keadaan.
bummzack
3
Itu berhasil, tapi kemudian Anda melihat dari Action untuk setiap tindakan unik. Ini juga mengasumsikan bahwa proses Anda cocok dengan antrian dengan baik - jika Anda ingin bercabang atau loop dengan kondisi akhir yang tidak ditentukan, solusi antrian rusak dengan cepat. Ini tentu saja lebih bersih daripada pendekatan mesin negara, tapi saya pikir coroutine masih lebih mudah dibaca :)
ZorbaTHut
Benar, tetapi untuk contoh Tetris itu sudah cukup :)
bummzack
Co-rutinitas rock- tetapi Lua sebagai bahasa menyebalkan dalam banyak hal lain, saya tidak bisa merekomendasikannya.
DeadMG
Selama Anda hanya perlu menghasilkan di tingkat atas (dan bukan dari pemanggilan fungsi bersarang), Anda dapat mencapai hal yang sama dalam C #, tapi ya, Lua coroutines rock.
murah hati
8

Saya mengambil ini dari Game Coding Lengkap oleh Mike McShaffry.

Dia berbicara tentang 'Manajer Proses', yang bermuara pada daftar tugas yang perlu dilakukan. Misalnya, suatu proses akan mengontrol animasi untuk menggambar pedang (AnimProcess), atau membuka pintu, atau dalam kasus Anda, membuat baris menghilang.

Proses akan ditambahkan ke daftar manajer proses, yang akan diulang setiap frame dan Pembaruan () dipanggil masing-masing. Entitas yang sangat mirip, tetapi untuk tindakan. Akan ada bendera kill untuk dihapus dari daftar ketika sudah selesai.

Hal lain yang rapi tentang mereka adalah bagaimana mereka dapat terhubung, dengan memiliki pointer ke proses selanjutnya. Dengan cara ini, proses baris animate Anda sebenarnya terdiri dari:

  • Proses Animasi untuk menghilangnya baris
  • A ProcessProcess untuk menghapus potongan
  • Proses Skor untuk menambahkan poin ke skor

(Karena proses dapat berupa hal-hal sekali pakai, kondisional di sana, atau di sana untuk jumlah waktu X)

Jika Anda ingin lebih detail, tanyakan saja.

Bebek Komunis
sumber
3

Anda dapat menggunakan antrian tindakan prioritas. Anda mendorong suatu tindakan, dan waktu. Setiap frame, Anda mendapatkan waktu, dan Anda menghapus semua tindakan yang memiliki waktu yang ditentukan seperti sebelumnya dan menjalankannya. Bonus: Pendekatan sejajar dengan baik, dan Anda benar-benar dapat menerapkan hampir semua logika game dengan cara ini.

DeadMG
sumber
1

Anda selalu perlu tahu perbedaan waktu antara frame sebelumnya dan saat ini, maka Anda harus melakukan dua hal.

-Putuskan kapan untuk memperbarui model Anda: mis. di tetris ketika penghapusan baris dimulai, Anda tidak ingin hal-hal bertabrakan dengan baris lagi, jadi Anda menghapus baris dari 'model' aplikasi Anda.

-Anda kemudian harus menangani objek yang berada dalam keadaan transisi ke kelas terpisah yang menyelesaikan animasi / acara selama periode waktu tertentu. Pada contoh tetris Anda akan membuat baris memudar perlahan (ubah sedikit kekeruhan setiap frame sedikit). Setelah opaqueness adalah 0, Anda mentransfer semua blok di atas baris satu ke bawah.

Ini mungkin tampak sedikit rumit pada awalnya, tetapi Anda akan memahami ini, pastikan untuk banyak abstrak di kelas yang berbeda, ini akan membuatnya lebih mudah. Pastikan juga bahwa peristiwa yang membutuhkan waktu, seperti menghilangkan baris dalam tetris, adalah jenis "Fire and Forget", buat saja objek baru yang menangani semua yang perlu dilakukan secara otomatis dan ketika semuanya dilakukan, menghapus sendiri dari scenegraph Anda.

Roy T.
sumber
Juga, dalam beberapa kasus perhitungan berat mungkin melebihi waktu yang diizinkan untuk satu langkah waktu fisika (mis. Deteksi tabrakan dan perencanaan jalur). Dalam kasus ini, Anda dapat melompat keluar dari perhitungan ketika waktu yang diberikan telah digunakan dan melanjutkan perhitungan frame berikutnya.
Nailer
0

Anda harus menganggap permainan sebagai "mesin negara yang terbatas". Gim ini dapat berada di salah satu dari beberapa negara bagian: dalam kasus Anda, "mengharapkan input", "bagian bergerak ke bawah", "baris meledak".

Anda melakukan berbagai hal tergantung pada negara. Misalnya, selama "bagian bergerak ke bawah" Anda mengabaikan input pemain, dan sebaliknya menghidupkan bagian dari baris saat ini ke baris berikutnya. Sesuatu seperti ini:

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
ggambett
sumber