Các hành động trò chơi cần nhiều khung hình để hoàn thành


20

Tôi chưa bao giờ thực sự làm nhiều chương trình trò chơi trước đây, câu hỏi khá đơn giản.

Hãy tưởng tượng tôi đang xây dựng một trò chơi Tetris, với vòng lặp chính trông giống như thế này.

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

Mọi thứ trong trò chơi cho đến nay đều xảy ra ngay lập tức - mọi thứ được sinh ra ngay lập tức, các hàng được xóa ngay lập tức, v.v ... Nhưng nếu tôi không muốn mọi thứ xảy ra ngay lập tức (tức là những thứ hoạt hình) thì sao?

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

Trong bản sao Pông của tôi, đây không phải là vấn đề, vì mọi khung hình tôi chỉ đang di chuyển quả bóng và kiểm tra va chạm.

Làm thế nào tôi có thể quấn đầu xung quanh vấn đề này? Chắc chắn hầu hết các trò chơi liên quan đến một số hành động mất nhiều hơn một khung và những thứ khác dừng lại cho đến khi hành động được thực hiện.

Câu trả lời:


11

Giải pháp truyền thống cho vấn đề này là một máy trạng thái hữu hạn, đang được đề xuất trong một số ý kiến.

Tôi ghét máy nhà nước hữu hạn.

Chắc chắn, chúng đơn giản, chúng được hỗ trợ trong mọi ngôn ngữ, nhưng chúng là một nỗi đau đáng kinh ngạc khi làm việc. Mọi thao tác đều mất rất nhiều mã sao chép và dán lỗi, và điều chỉnh hiệu ứng theo những cách nhỏ có thể là một thay đổi lớn đối với mã.

Nếu bạn có thể sử dụng một ngôn ngữ hỗ trợ họ, tôi khuyên bạn nên dùng coroutines. Họ cho phép bạn viết mã trông giống như:

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

Rõ ràng là khá giả mã, nhưng rõ ràng đây không chỉ là một mô tả tuyến tính đơn giản về hiệu ứng đặc biệt, mà nó còn dễ dàng cho phép chúng ta thả một khối mới trong khi hoạt hình vẫn đang hoàn thiện . Hoàn thành việc này với một máy trạng thái nói chung sẽ rất khủng khiếp.

Theo hiểu biết tốt nhất của tôi, chức năng này không dễ dàng có sẵn trong C, C ++, C #, Objective C hoặc Java. Đây là một trong những lý do chính khiến tôi sử dụng Lua cho tất cả logic trò chơi của mình :)


Bạn cũng có thể thực hiện một cái gì đó dọc theo các dòng này trong các ngôn ngữ OOP khác. Hãy tưởng tượng một số loại của một Actionlớp và một hàng các hành động để thực hiện. Khi một hành động hoàn tất, loại bỏ nó khỏi hàng đợi và thực hiện hành động tiếp theo, vv Cách linh hoạt hơn một máy trạng thái.
bummzack

3
Điều đó có hiệu quả, nhưng sau đó bạn đang xem xét xuất phát từ Hành động cho từng hành động duy nhất. Nó cũng giả định rằng quy trình của bạn phù hợp với một hàng đợi độc đáo - nếu bạn muốn phân nhánh hoặc các vòng lặp với các điều kiện kết thúc không xác định, giải pháp hàng đợi bị hỏng nhanh chóng. Nó chắc chắn sạch hơn so với cách tiếp cận của máy trạng thái, nhưng tôi nghĩ rằng coroutines vẫn vượt trội về khả năng đọc :)
ZorbaTHut

Đúng, nhưng đối với ví dụ Tetris thì nó là đủ :)
bummzack

Co-thường xuyên rock- nhưng Lua như một ngôn ngữ hút theo nhiều cách khác, tôi chỉ không thể khuyên bạn nên nó.
DeadMG

Miễn là bạn chỉ cần đạt năng suất ở mức cao nhất (chứ không phải từ lệnh gọi hàm lồng nhau), bạn có thể thực hiện điều tương tự trong C #, nhưng vâng, Lua coroutines rock.
khoan hồng

8

Tôi đang lấy cái này từ Game Coding Complete của Mike McShaffry.

Anh ta nói về một 'Trình quản lý quy trình', trong đó liệt kê một danh sách các nhiệm vụ cần phải hoàn thành. Ví dụ, một quy trình sẽ điều khiển hoạt hình để vẽ một thanh kiếm (AnimProcess) hoặc mở một cánh cửa, hoặc trong trường hợp của bạn, làm cho hàng biến mất.

Quá trình sẽ được thêm vào danh sách của người quản lý quy trình, sẽ được lặp lại mỗi khung và Cập nhật () được gọi trên mỗi khung. Vì vậy, các thực thể rất giống, nhưng cho hành động. Sẽ có một cờ kill để xóa khỏi danh sách khi nó kết thúc.

Một điều thú vị khác về họ là làm thế nào họ có thể liên kết, bằng cách có một con trỏ đến quy trình tiếp theo. Theo cách này, quy trình hàng động của bạn thực sự có thể bao gồm:

  • Một AnimationProcess cho hàng biến mất
  • MovementProcess để loại bỏ các mảnh
  • ScoreProcess để thêm điểm vào điểm số

(Vì các quy trình có thể là những thứ sử dụng một lần, có điều kiện ở đó hoặc ở đó trong khoảng thời gian X)

Nếu bạn muốn biết thêm chi tiết, hãy hỏi đi.


3

Bạn có thể sử dụng một hàng đợi hành động ưu tiên. Bạn thúc đẩy trong một hành động, và một thời gian. Mỗi khung hình, bạn có được thời gian và bạn bật ra tất cả các hành động có thời gian được chỉ định như trước thời điểm đó và thực hiện chúng. Phần thưởng: Tiếp cận song song độc đáo và bạn thực sự có thể thực hiện gần như tất cả logic trò chơi theo cách này.


1

Bạn luôn cần biết chênh lệch thời gian giữa khung trước và khung hiện tại, sau đó bạn phải làm hai việc.

-Decide khi cập nhật mô hình của bạn: vd. trong tetris khi bắt đầu xóa hàng, bạn không muốn công cụ va chạm với hàng nữa, vì vậy bạn xóa hàng khỏi 'mô hình' của ứng dụng của bạn.

-Bạn sau đó phải xử lý đối tượng đang ở trạng thái chuyển sang một lớp riêng để giải quyết hoạt hình / sự kiện trong một khoảng thời gian. Trong ví dụ tetris, bạn sẽ có hàng mờ dần (thay đổi độ mờ mỗi khung hình một chút). Sau khi độ mờ là 0, bạn chuyển tất cả các khối trên đầu hàng xuống.

Điều này có vẻ hơi phức tạp lúc đầu, nhưng bạn sẽ hiểu rõ điều này, chỉ cần đảm bảo trừu tượng hóa rất nhiều trong các lớp khác nhau, điều này sẽ làm cho nó dễ dàng hơn. Ngoài ra, hãy đảm bảo rằng các sự kiện mất thời gian, như loại bỏ một hàng trong tetris, thuộc loại "Lửa và Quên", chỉ cần tạo một đối tượng mới xử lý mọi thứ cần được thực hiện tự động và khi mọi thứ được thực hiện, loại bỏ chính nó khỏi khung cảnh của bạn.


Ngoài ra, trong một số trường hợp, các tính toán nặng có thể vượt quá thời gian cho phép đối với một bước thời gian vật lý (ví dụ: phát hiện va chạm và lập kế hoạch đường đi). Trong những trường hợp này, bạn có thể nhảy ra khỏi các phép tính khi thời gian được phân bổ đã được sử dụng và tiếp tục tính toán khung tiếp theo.
Thợ làm móng

0

Bạn cần nghĩ về trò chơi như một "cỗ máy trạng thái hữu hạn". Trò chơi có thể ở một trong một số trạng thái: trong trường hợp của bạn, "mong đợi đầu vào", "mảnh di chuyển xuống", "nổ tung hàng".

Bạn làm những việc khác nhau tùy thuộc vào nhà nước. Ví dụ: trong khi "mảnh di chuyển xuống", bạn bỏ qua đầu vào của người chơi và thay vào đó làm động các mảnh từ hàng hiện tại của nó sang hàng tiếp theo. Một cái gì đó như thế này:

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.