Tách logic / cập nhật khỏi mã kết xuất / vẽ trong một luồng bằng cách sử dụng chế độ ngủ


9

Tôi đã đọc rằng tốc độ của các đối tượng trò chơi không nên bị cản trở bởi FPS mà thay vào đó nên dựa trên thời gian. Làm cách nào tôi có thể tách riêng mã cập nhật / vẽ để tối đa hóa hiệu suất mà không giới hạn tốc độ vẽ và cung cấp tốc độ cập nhật logic không đổi dựa trên thời gian?

Mã giả hiện tại của tôi là như sau

loop
{
    draw();
    if (ticksElapsed() > 100)
    {
        update();
        ticks+= ticksElapsed();
    }        
}

Vấn đề là mã bản vẽ cản trở hiệu suất của tốc độ update (). Và nó tiêu thụ 100% cpu vì nếu giấc ngủ bị ném vào, nó sẽ loại bỏ cả chức năng vẽ / logic.

Tôi cũng đang sử dụng SDL và dường như không có tùy chọn vsync. Tôi cũng đã nghe nói về các thuật ngữ cố định và bước thời gian thay đổi tuy nhiên tôi không chắc làm thế nào có thể được thực hiện với giấc ngủ ()


1
Bạn không cần lãng phí 100% năng lượng CPU chỉ để chờ đợi, đặt chế độ ngủ (0) vào cuối vòng lặp while nếu ticksElapsed () <100. Hệ điều hành sẽ quay lại luồng ngay lập tức nếu không có luồng nào khác muốn chạy Nhưng không lãng phí 100% năng lượng CPU nữa.
Maik

Tuy nhiên, giải pháp tốt nhất cho thiết lập 1 luồng như vậy là sử dụng vsync, nếu bạn không thể vsync, sau đó gọi ngủ (0) trong một vòng lặp cho đến khi bạn đạt được tốc độ khung hình đích, sau đó cập nhật và vẽ
Maik Semder

Câu trả lời:


3

Trong đoạn mã của bạn, có vẻ như bạn đang cố gắng chạy trò chơi của mình ở chế độ bước thời gian cố định bằng cách chờ đợi bận rộn nếu bản vẽ và cập nhật của bạn mất ít hơn 15ms (60fps). Điều này là có thể và bạn đã đoán đúng rằng điều này không thể được thực hiện bằng cách sử dụng một cuộc gọi ngủ vì bạn không biết chính xác mình sẽ ngủ bao lâu. Vòng lặp bận rộn chờ đợi là giải pháp tốt.

Tuy nhiên, hãy xem xét trường hợp cập nhật và vẽ của bạn vượt quá 15ms, bây giờ bạn có bản vẽ và cập nhật trò chơi chậm. Bây giờ bạn có thể thực hiện một trong hai điều sau: phát hiện trạng thái này và thả khung hình (bỏ qua bản vẽ và chuyển thẳng đến cập nhật cho đến khi bạn đồng bộ lại) tuy nhiên nếu máy tính chỉ chậm thì sẽ không bao giờ bắt kịp.

Một giải pháp khác là làm cho logic cập nhật của bạn độc lập với thời gian cố định. Bạn không cần một chủ đề riêng cho việc này, bạn chỉ cần xác nhận lại mọi thứ sẽ diễn ra nhanh như thế nào. Thay vì 5 pixel mỗi tick, bạn nên sử dụng 50 pixel mỗi giây. Bạn sẽ cần một bộ hẹn giờ có độ chính xác cao để đạt được điều này và tất cả logic cập nhật của bạn sẽ có thể truy cập vào bộ hẹn giờ để xem thời gian trôi qua kể từ lần cập nhật trước.

Về cơ bản bạn đi từ:

void UpdatePlayer()
 player.x += 10;

Đến

void UpdatePlayer(float elapsedSeconds) //the total seconds elapsed since last update
 player.x += walkspeed * elapsedSeconds;

Vì vậy, động cơ của tôi sẽ luôn tiêu thụ 100% và tôi thực sự không thể làm gì về nó?
Oskenso Kashi

1
Nó sẽ tiêu tốn nhiều chu kỳ như trình lập lịch cho phép, nhưng đó không thực sự là vấn đề vì bạn thực sự không thể làm nhiều việc khác trong khi bạn đang chơi game :).
Roy T.

@Oskenso Tuy nhiên, có vấn đề nếu bạn sử dụng nhiều hơn 1 luồng, thì luồng chính sẽ không cho phép các luồng khác chạy hết mức có thể, lãng phí rất nhiều sức mạnh tính toán trong vòng lặp while, bạn thực sự nên xem xét một giấc ngủ
Học kỳ Maik

@Maik Semder: bạn có giải pháp nào cho giấc ngủ (x) không chính xác không? Sau khi khoảng thời gian ngủ đã trôi qua, chuỗi đã sẵn sàng để chạy. Nhưng một chủ đề sẵn sàng không được đảm bảo để chạy ngay lập tức. Điều đó tùy thuộc vào lịch trình. Khi bạn đang sử dụng hai chủ đề, có các giải pháp khác, để xem bài viết tuyệt vời này: altdevblogaday.com/2011/07/03/threading-and-your-game-loop
Roy T.

1
@Roy ngủ (0) là giải pháp. Nó trả về ngay lập tức nếu không có luồng nào khác muốn chạy ( Sleep WinAPI ) và cho các luồng khác cơ hội chạy. Nếu luồng khác sẽ không cho luồng chính có cơ hội chạy lại, thì bạn có vấn đề về luồng, nhưng việc chặn mọi thứ khác bằng cách không gọi giấc ngủ ở vị trí đầu tiên khiến nó thậm chí còn tồi tệ hơn và hầu như không phải là giải pháp. Điều quan trọng là gọi chế độ ngủ (0) và kiểm tra thời gian đã trôi qua cho đến khi bạn đạt tốc độ khung hình cố định mục tiêu, do đó bạn không lãng phí 100% CPU chỉ để chờ đợi.
Maik
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.