Vòng lặp trò chơi Optimal tối ưu cho trò chơi phụ 2D


14

Có thể mô tả bố cục "tối ưu" (về hiệu suất) cho vòng lặp trò chơi của máy cuộn phụ 2D không? Trong bối cảnh này, "vòng lặp trò chơi" lấy đầu vào của người dùng, cập nhật trạng thái của các đối tượng trò chơi và vẽ các đối tượng trò chơi.

Ví dụ: có một lớp cơ sở GameObject với hệ thống phân cấp thừa kế sâu có thể tốt cho việc bảo trì ... bạn có thể làm một cái gì đó như sau:

foreach(GameObject g in gameObjects) g.update();

Tuy nhiên tôi nghĩ cách tiếp cận này có thể tạo ra các vấn đề hiệu suất.

Mặt khác, tất cả dữ liệu và chức năng của các đối tượng trò chơi có thể là toàn cầu. Đó sẽ là một vấn đề đau đầu bảo trì nhưng có thể gần hơn với một vòng lặp trò chơi hiệu quả tối ưu.

Có suy nghĩ gì không? Tôi quan tâm đến các ứng dụng thực tế của cấu trúc vòng lặp trò chơi gần tối ưu ... ngay cả khi tôi phải đau đầu bảo trì để đổi lấy hiệu suất tuyệt vời.


Câu trả lời:


45

Ví dụ: có một lớp cơ sở GameObject với hệ thống phân cấp thừa kế sâu có thể tốt cho việc bảo trì ...

Trên thực tế, hệ thống phân cấp sâu thường kém hơn về khả năng bảo trì so với nông cạn và phong cách kiến ​​trúc hiện đại cho các đối tượng trò chơi đang có xu hướng hướng tới các phương pháp tiếp cận dựa trên tổng hợp .

Tuy nhiên tôi nghĩ cách tiếp cận này có thể tạo ra các vấn đề hiệu suất. Mặt khác, tất cả dữ liệu và chức năng của các đối tượng trò chơi có thể là toàn cầu. Đó sẽ là một vấn đề đau đầu bảo trì nhưng có thể gần hơn với một vòng lặp trò chơi hiệu quả tối ưu.

Vòng lặp mà bạn đã thể hiện có khả năng có vấn đề về hiệu năng, nhưng không, như được ngụ ý bởi câu lệnh tiếp theo của bạn, bởi vì bạn có dữ liệu cá thể và các hàm thành viên trong GameObjectlớp. Thay vào đó, vấn đề là nếu bạn đối xử với mọi đối tượng trong trò chơi giống hệt nhau, thì có lẽ bạn không nhóm các đối tượng đó một cách thông minh - chúng có thể nằm rải rác ngẫu nhiên trong danh sách đó. Sau đó, có khả năng, mọi cuộc gọi đến phương thức cập nhật cho đối tượng đó (cho dù phương thức đó có phải là hàm toàn cục hay không và liệu đối tượng có dữ liệu cá thể hay "dữ liệu toàn cầu" trôi nổi trong một số bảng mà bạn đang lập chỉ mục hoặc bất cứ điều gì không) khác với lệnh gọi cập nhật trong các vòng lặp lặp cuối cùng.

Điều này có thể gây áp lực lên hệ thống vì bạn có thể cần trang bộ nhớ với chức năng liên quan vào và ra khỏi bộ nhớ và điền lại bộ đệm lệnh thường xuyên hơn, dẫn đến vòng lặp chậm hơn. Có hay không này là quan sát được bằng mắt thường (hoặc thậm chí trong một hồ sơ) phụ thuộc vào chính xác những gì được coi là một một "trò chơi đối tượng," có bao nhiêu trong số họ tồn tại trên trung bình, và những gì khác đang xảy ra trong ứng dụng của bạn.

Các hệ thống đối tượng hướng thành phần là một xu hướng phổ biến hiện nay, thúc đẩy triết lý rằng tập hợp được ưu tiên hơn kế thừa . Các hệ thống như vậy có khả năng cho phép bạn phân chia logic "cập nhật" của các thành phần (trong đó "thành phần" được định nghĩa đại khái là một số đơn vị chức năng, chẳng hạn như phần thể hiện phần mô phỏng vật lý của một số đối tượng, được xử lý bởi hệ thống vật lý ) trên nhiều luồng - được phân biệt bởi loại thành phần - nếu có thể và mong muốn, có thể có hiệu suất tăng. Ít nhất bạn có thể tổ chức các thành phần sao cho tất cả các thành phần của một kiểu đã cho cập nhật cùng nhau , sử dụng tối ưu bộ đệm của CPU. Một ví dụ về một hệ thống định hướng thành phần như vậy được thảo luận trong chủ đề này .

Các hệ thống như vậy thường được phân tách cao, cũng là một lợi ích để bảo trì.

Thiết kế hướng dữ liệu là một cách tiếp cận có liên quan - đó là về việc tự định hướng xung quanh dữ liệu cần thiết của các đối tượng là ưu tiên hàng đầu, để dữ liệu có thể được xử lý hàng loạt một cách hiệu quả (ví dụ). Điều này thường có nghĩa là một tổ chức cố gắng giữ dữ liệu được sử dụng cho cùng một cụm mục đích và hoạt động cùng một lúc. Về cơ bản, nó không tương thích với thiết kế OO và bạn có thể tìm thấy một số câu chuyện phiếm về chủ đề ở đây tại GDSE trong câu hỏi này .

Trong thực tế, một cách tiếp cận tối ưu hơn cho vòng lặp trò chơi sẽ là, thay vì ban đầu của bạn

foreach(GameObject g in gameObjects) g.update();

một cái gì đó giống như

ProcessUserInput();
UpdatePhysicsForAllObjects();
UpdateScriptsForAllObjects();
UpdateRenderDataForAllObjects();
RenderEverything();

Trong một thế giới như vậy, mỗi GameObjectcó thể có một con trỏ hoặc tham chiếu đến riêng của mình PhysicsDatahoặc Scripthoặc RenderData, đối với các trường hợp mà bạn có thể cần phải tương tác với các đối tượng trên cơ sở cá nhân, nhưng thực tế PhysicsData, Scripts, RenderData, vân vân tất cả sẽ được sở hữu bởi hệ thống con của mình (giả lập vật lý, môi trường lưu trữ tập lệnh, trình kết xuất) và được xử lý hàng loạt như được chỉ ra ở trên.

Điều rất quan trọng cần lưu ý là cách tiếp cận này không phải là một viên đạn ma thuật và sẽ không luôn mang lại sự cải thiện hiệu suất (mặc dù nó thường là một thiết kế tốt hơn so với cây thừa kế sâu). Về cơ bản, bạn đặc biệt nhận thấy không có sự khác biệt về hiệu năng nếu bạn có rất ít đối tượng hoặc rất nhiều đối tượng bạn không thể song song hóa hiệu quả các bản cập nhật.

Thật không may, không có vòng lặp ma thuật nào là tối ưu nhất - mỗi trò chơi đều khác nhau và có thể cần điều chỉnh hiệu suất theo những cách khác nhau. Vì vậy, rất quan trọng để đo lường (hồ sơ) mọi thứ trước khi bạn mù quáng nhận lời khuyên của một số người ngẫu nhiên trên internet.


5
Chúa tể, đây là một câu trả lời tuyệt vời.
Raveline

2

Phong cách lập trình đối tượng trò chơi phù hợp với các dự án nhỏ hơn. Khi dự án trở nên thực sự lớn, bạn sẽ kết thúc với hệ thống phân cấp kế thừa sâu sắc và cơ sở đối tượng Trò chơi sẽ trở nên thực sự nặng nề!

Ví dụ ... Giả sử bạn đã bắt đầu tạo cơ sở đối tượng Trò chơi với các thuộc tính tối thiểu là Position (cho x và y), displayObject (điều này đề cập đến hình ảnh nếu là thành phần vẽ), chiều rộng, chiều cao, v.v ...

Bây giờ sau này nếu chúng ta cần thêm một lớp con sẽ có trạng thái hoạt hình, thì bạn có thể di chuyển 'mã điều khiển hoạt hình' (giả sử currentAnimationId, Số khung cho hoạt hình này, v.v.) vào GameObjectBase trong suy nghĩ rằng hầu hết các đối tượng sẽ có hình ảnh động.
Sau này nếu bạn muốn thêm phần thân cứng tĩnh (không có bất kỳ hình động nào), bạn cần kiểm tra 'mã điều khiển hoạt hình' trong chức năng cập nhật của GameObject Base (mặc dù nó có kiểm tra xem hoạt hình có ở đó hay không. .. vấn đề!) Trái ngược với điều này nếu bạn theo Cấu trúc dựa trên Thành phần, bạn sẽ có một 'Thành phần điều khiển hoạt hình' được gắn vào đối tượng của mình. Nếu cần, bạn sẽ đính kèm. Đối với phần thân tĩnh, bạn sẽ không đính kèm thành phần đó. cách, chúng ta có thể tránh kiểm tra không cần thiết.

Vì vậy, việc lựa chọn kiểu dáng phụ thuộc vào kích thước của dự án. Cấu trúc trò chơi chỉ dễ dàng để làm chủ cho các dự án nhỏ hơn. Hy vọng điều này sẽ giúp một chút.


@Josh Petrie - Thực sự là một câu trả lời tuyệt vời!
Ayyappa

@Josh Petrie - Câu trả lời thực sự tốt với các liên kết hữu ích! (Không biết cách đặt bình luận này bên cạnh bài đăng của bạn: P)
Ayyappa

Thông thường, bạn có thể nhấp vào liên kết "thêm nhận xét" trong câu trả lời của tôi, nhưng điều đó đòi hỏi danh tiếng nhiều hơn bạn hiện có (xem gamedev.stackexchange.com/priv đặc biệt / comment ). Và cảm ơn!
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.