Bạn muốn tách riêng cập nhật (đánh dấu logic) và vẽ tỷ lệ (kết xuất đánh dấu).
Cập nhật của bạn sẽ tạo ra vị trí của tất cả các đối tượng trên thế giới sẽ được rút ra.
Tôi sẽ đề cập đến hai khả năng khác nhau ở đây, một khả năng bạn yêu cầu, ngoại suy và một phương pháp khác là nội suy.
1.
Phép ngoại suy là nơi chúng ta sẽ tính toán vị trí (dự đoán) của đối tượng ở khung tiếp theo, sau đó nội suy giữa vị trí đối tượng hiện tại và vị trí mà đối tượng sẽ ở khung tiếp theo.
Để làm điều này, mỗi đối tượng được vẽ phải có một liên kết velocity
và position
. Để tìm vị trí mà đối tượng sẽ ở khung tiếp theo, chúng ta chỉ cần thêm velocity * draw_timestep
vào vị trí hiện tại của đối tượng, để tìm vị trí dự đoán của khung tiếp theo. draw_timestep
là lượng thời gian đã trôi qua kể từ lần đánh dấu kết xuất trước đó (còn gọi là lệnh rút thăm trước đó).
Nếu bạn để nó ở đây, bạn sẽ thấy các đối tượng "nhấp nháy" khi vị trí dự đoán của chúng không khớp với vị trí thực tế ở khung tiếp theo. Để loại bỏ nhấp nháy, bạn có thể lưu trữ vị trí dự đoán và lerp giữa vị trí dự đoán trước đó và vị trí dự đoán mới ở mỗi bước vẽ, sử dụng thời gian trôi qua kể từ lần đánh dấu cập nhật trước đó làm yếu tố lerp. Điều này vẫn sẽ dẫn đến hành vi kém khi các đối tượng chuyển động nhanh đột ngột thay đổi vị trí và bạn có thể muốn xử lý trường hợp đặc biệt đó. Tất cả những gì được nói trong đoạn này là lý do tại sao bạn không muốn sử dụng phép ngoại suy.
2.
Nội suy là nơi chúng tôi lưu trữ trạng thái của hai bản cập nhật mới nhất và nội suy giữa chúng dựa trên lượng thời gian hiện tại đã trôi qua kể từ bản cập nhật trước đó. Trong thiết lập này, mỗi đối tượng phải có một liên kết position
và previous_position
. Trong trường hợp này, bản vẽ của chúng tôi sẽ đại diện cho một bản cập nhật tồi tệ nhất phía sau trò chơi hiện tại và tốt nhất là ở trạng thái chính xác như bản cập nhật hiện tại.
Theo tôi, có lẽ bạn muốn nội suy như tôi đã mô tả, vì hai bên dễ thực hiện hơn và rút ra một phần rất nhỏ của một giây (ví dụ 1/60 giây) phía sau trạng thái cập nhật hiện tại của bạn là ổn.
Chỉnh sửa:
Trong trường hợp ở trên không đủ để cho phép bạn thực hiện triển khai, đây là một ví dụ về cách thực hiện phương pháp nội suy mà tôi đã mô tả. Tôi sẽ không bao gồm ngoại suy, bởi vì tôi không thể nghĩ ra bất kỳ kịch bản trong thế giới thực nào mà bạn nên thích nó.
Khi bạn tạo một đối tượng có thể vẽ, nó sẽ lưu trữ các thuộc tính cần thiết để vẽ (tức là thông tin trạng thái cần thiết để vẽ nó).
Trong ví dụ này, chúng tôi sẽ lưu trữ vị trí và xoay. Bạn cũng có thể muốn lưu trữ các thuộc tính khác như vị trí tọa độ màu hoặc kết cấu (nghĩa là nếu một họa tiết cuộn).
Để ngăn dữ liệu bị sửa đổi trong khi luồng kết xuất đang vẽ nó, (tức là vị trí của một đối tượng bị thay đổi trong khi luồng kết xuất vẽ, nhưng tất cả các đối tượng khác chưa được cập nhật), chúng ta cần thực hiện một số loại bộ đệm đôi.
Một đối tượng lưu trữ hai bản sao của nó previous_state
. Tôi sẽ đặt chúng trong một mảng và gọi chúng là previous_state[0]
và previous_state[1]
. Nó tương tự cần hai bản sao của nó current_state
.
Để theo dõi bản sao của bộ đệm đôi được sử dụng, chúng tôi lưu trữ một biến state_index
, có sẵn cho cả luồng cập nhật và vẽ.
Trước tiên, luồng cập nhật sẽ tính toán tất cả các thuộc tính của một đối tượng bằng dữ liệu của chính nó (bất kỳ cấu trúc dữ liệu nào bạn muốn). Sau đó, nó sao chép current_state[state_index]
đến previous_state[state_index]
, và sao chép dữ liệu mới phù hợp cho bản vẽ, position
và rotation
vào current_state[state_index]
. Sau đó state_index = 1 - state_index
, để lật bản sao hiện đang sử dụng của bộ đệm đôi.
Tất cả mọi thứ trong đoạn trên phải được thực hiện với một khóa được lấy ra current_state
. Các bản cập nhật và vẽ chủ đề đều đưa ra khóa này. Khóa chỉ được lấy ra trong thời gian sao chép thông tin trạng thái, nhanh.
Trong luồng kết xuất, sau đó bạn thực hiện phép nội suy tuyến tính trên vị trí và xoay như vậy:
current_position = Lerp(previous_state[state_index].position, current_state[state_index].position, elapsed/update_tick_length)
Đâu elapsed
là lượng thời gian đã trôi qua trong luồng kết xuất, kể từ lần cập nhật cuối cùng và update_tick_length
là khoảng thời gian mà tốc độ cập nhật cố định của bạn mất trên mỗi lần đánh dấu (ví dụ: tại các bản cập nhật 20FPS, update_tick_length = 0.05
).
Nếu bạn không biết Lerp
chức năng trên là gì, thì hãy kiểm tra bài viết của wikipedia về chủ đề: Nội suy tuyến tính . Tuy nhiên, nếu bạn không biết lerping là gì, thì có lẽ bạn chưa sẵn sàng để thực hiện cập nhật / bản vẽ tách rời với bản vẽ được nội suy.