Tại sao tôi nên tách các đối tượng khỏi kết xuất?


11

Disclamer: Tôi biết mô hình hệ thống thực thể là gì và tôi không sử dụng nó.

Tôi đã đọc rất nhiều về việc tách đối tượng và kết xuất. Về thực tế là logic trò chơi nên độc lập với công cụ kết xuất cơ bản. Đó là tất cả tốt đẹp và bảnh bao và nó có ý nghĩa hoàn hảo, nhưng nó cũng gây ra rất nhiều nỗi đau khác:

  • cần đồng bộ hóa giữa đối tượng logic và đối tượng kết xuất (đối tượng giữ trạng thái của hình ảnh động, các họa tiết, v.v.)
  • cần mở đối tượng logic ra công chúng để đối tượng kết xuất đọc trạng thái thực tế của đối tượng logic (thường dẫn đối tượng logic dễ dàng chuyển đổi trong đối tượng getter và setter câm)

Đây không phải là một giải pháp tốt cho tôi. Mặt khác, thật trực quan khi tưởng tượng một đối tượng là đại diện 3d (hoặc 2d) của nó và cũng rất dễ bảo trì (và có thể được gói gọn hơn nhiều).

Có cách nào để duy trì biểu diễn đồ họa và logic trò chơi được kết hợp với nhau (tránh các vấn đề đồng bộ hóa) nhưng lại trừu tượng hóa công cụ kết xuất không? Hoặc có cách nào để phân tách logic trò chơi và kết xuất mà không gây ra những hạn chế ở trên không?

(có thể với các ví dụ, tôi không giỏi trong việc hiểu các cuộc nói chuyện trừu tượng)


1
Nó cũng sẽ hữu ích nếu bạn cung cấp một ví dụ về ý của bạn khi bạn nói rằng bạn không sử dụng mẫu hệ thống thực thể và cách bạn nghĩ có liên quan đến việc bạn có nên tách biệt mối quan tâm về kết xuất khỏi mối quan tâm của thực thể / logic trò chơi.
michael.bartnett

@ michael.bartnett, Tôi không tách các đối tượng trong các thành phần nhỏ có thể tái sử dụng được xử lý bởi các hệ thống, cách thức thực hiện hầu hết các mẫu. Thay vào đó, mã của tôi là một nỗ lực cho mẫu MVC. Nhưng nó không thực sự quan trọng vì câu hỏi không phụ thuộc vào bất kỳ mã nào (thậm chí không phải là ngôn ngữ). Tôi đã đặt từ chối vì tôi biết một số người đã cố gắng thuyết phục tôi sử dụng ECS, điều đó dường như để chữa ung thư. Và, như bạn có thể thấy, dù sao nó cũng đã xảy ra.
Giày

Câu trả lời:


13

Giả sử bạn có một cảnh bao gồm một thế giới , một người chơi và một ông chủ. Ồ, và đây là trò chơi của người thứ ba, vì vậy bạn cũng có một máy ảnh .

Vì vậy, cảnh của bạn trông như thế này:

class Scene {
    World* world
    Player* player
    Enemy* boss
    Camera* camera
}

(Ít nhất, đó là dữ liệu cơ bản . Cách bạn chứa dữ liệu, tùy thuộc vào bạn.)

Bạn chỉ muốn cập nhật và kết xuất cảnh khi bạn đang chơi trò chơi, không phải khi bị tạm dừng hoặc trong menu chính ... vì vậy bạn gắn nó vào trạng thái trò chơi!

State* gameState = new State();
gameState->addScene(scene);

Bây giờ trạng thái trò chơi của bạn có một cảnh. Tiếp theo, bạn muốn chạy logic trên cảnh và kết xuất cảnh. Đối với logic, bạn chỉ cần chạy một chức năng cập nhật.

State::update(double delta) {
    scene->update(delta);
}

Bằng cách đó bạn có thể giữ tất cả logic trò chơi trong Scenelớp. Và chỉ để tham khảo, một hệ thống thành phần thực thể có thể làm điều đó như thế này:

State::update(double delta) {
    physicsSystem->applyPhysics(scene);
}

Dù sao, bây giờ bạn đã quản lý để cập nhật cảnh của bạn. Bây giờ bạn muốn hiển thị nó! Mà chúng tôi làm một cái gì đó tương tự như trên:

State::render() {
    renderSystem->render(scene);
}

Có bạn đi. Hệ thống renderS đọc thông tin từ cảnh và hiển thị hình ảnh phù hợp. Đơn giản hóa, phương thức hiển thị cảnh có thể trông như thế này:

RenderSystem::renderScene(Scene* scene) {
    Camera* camera = scene->camera;
    lookAt(camera); // Set up the appropriate viewing matrices based on 
                    // the camera location and direction

    renderHeightmap(scene->getWorld()->getHeightMap()); // Just as an example, you might
                                                        // use a height map as your world
                                                        // representation.
    renderModel(scene->getPlayer()->getType()); // getType() will return, for example "orc"
                                                // or "human"

    renderModel(scene->getBoss()->getType());
}

Thực sự đơn giản, bạn vẫn cần, ví dụ, áp dụng xoay và dịch dựa trên vị trí của người chơi của bạn và nơi anh ấy đang tìm kiếm. (Ví dụ của tôi là một trò chơi 3D, nếu bạn chơi 2D, thì đó sẽ là một cuộc dạo chơi trong công viên).

Tôi hy vọng đó là điều mà bạn đang tìm kiếm? Như bạn có thể hy vọng hồi tưởng từ những điều trên, hệ thống kết xuất không quan tâm đến logic của trò chơi . Nó chỉ sử dụng trạng thái hiện tại của cảnh để kết xuất, tức là nó lấy thông tin cần thiết từ nó, để hiển thị. Còn logic trò chơi? Nó không quan tâm những gì trình kết xuất làm. Heck, nó không quan tâm nếu nó hiển thị ở tất cả!

Và bạn cũng không cần phải đính kèm thông tin kết xuất vào cảnh. Nó phải đủ để trình kết xuất biết rằng nó cần kết xuất một con Orc. Bạn đã tải một mô hình orc rồi, mà trình kết xuất sau đó biết để hiển thị.

Điều này sẽ đáp ứng yêu cầu của bạn. Biểu diễn đồ họa và logic được ghép nối , bởi vì cả hai đều sử dụng cùng một dữ liệu. Tuy nhiên, họ là riêng biệt , bởi vì không dựa vào nhau!

EDIT: Và chỉ để trả lời tại sao một người sẽ làm điều đó như thế này? Bởi vì nó dễ dàng hơn là lý do đơn giản nhất. Bạn không cần phải suy nghĩ về "chuyện như vậy và đã xảy ra, giờ tôi nên cập nhật đồ họa". Thay vào đó, bạn biến mọi thứ thành hiện thực và mỗi khung hình trò chơi sẽ xem xét những gì đang diễn ra và diễn giải nó theo một cách nào đó, mang lại cho bạn kết quả trên màn hình.


7

Tiêu đề của bạn hỏi một câu hỏi khác với nội dung cơ thể của bạn. Trong tiêu đề, bạn hỏi tại sao nên tách biệt logic và kết xuất, nhưng trong phần thân bạn yêu cầu triển khai các hệ thống logic / đồ họa / kết xuất.

Câu hỏi thứ hai đã được giải quyết trước đây , vì vậy tôi sẽ tập trung vào câu hỏi đầu tiên.

Lý do phân tách logic và kết xuất:

  1. Quan niệm rộng rãi rằng các đối tượng nên làm một việc
  2. Nếu bạn muốn chuyển từ 2D sang 3D thì sao? Điều gì xảy ra nếu bạn quyết định thay đổi từ một hệ thống kết xuất này sang một hệ thống khác ở giữa dự án? Bạn không muốn thu thập dữ liệu tất cả các mã của mình và thực hiện các thay đổi lớn ở giữa logic trò chơi của mình.
  3. Bạn có thể có lý do để lặp lại các phần của mã, thường được coi là một ý tưởng tồi.
  4. Bạn có thể xây dựng các hệ thống để kiểm soát các luồng kết xuất hoặc logic có khả năng lớn mà không cần giao tiếp riêng lẻ với các phần nhỏ.
  5. Điều gì sẽ xảy ra nếu bạn muốn gán đá quý cho người chơi nhưng hệ thống bị chậm lại bởi bao nhiêu mặt đá quý có? Nếu bạn đã trừu tượng hóa hệ thống kết xuất của mình đủ tốt, bạn có thể cập nhật nó ở các mức giá khác nhau để tính đến các hoạt động kết xuất đắt tiền.
  6. Nó cho phép bạn suy nghĩ về những điều thực sự quan trọng với những gì bạn đang làm. Tại sao bộ não của bạn xoay quanh ma trận biến đổi và sprite bù đắp và tọa độ màn hình khi tất cả những gì bạn muốn làm là thực hiện một cơ chế nhảy kép, rút ​​thẻ hoặc trang bị một thanh kiếm? Bạn không muốn sprite đại diện cho thanh kiếm được trang bị của bạn có màu hồng sáng chỉ vì bạn muốn di chuyển nó từ tay phải sang trái.

Trong cài đặt OOP, khởi tạo các đối tượng mới có chi phí, nhưng theo kinh nghiệm của tôi, chi phí cho tài nguyên hệ thống là một cái giá nhỏ để trả cho khả năng suy nghĩ và thực hiện những điều cụ thể mà tôi cần phải hoàn thành.


6

Câu trả lời này chỉ để xây dựng trực giác về lý do tại sao tách biệt kết xuất và logic là quan trọng, thay vì trực tiếp đề xuất các ví dụ thực tế.

Giả sử chúng ta có một con voi lớn , không ai trong phòng có thể nhìn thấy toàn bộ con voi. có lẽ mọi người thậm chí không đồng ý với những gì nó thực sự là. Bởi vì mọi người đều nhìn thấy một phần khác nhau của con voi và chỉ có thể đối phó với phần đó. Nhưng cuối cùng, điều này không thay đổi sự thật rằng nó là một con voi lớn.

Con voi đại diện cho đối tượng trò chơi với tất cả các chi tiết của nó. Nhưng không ai thực sự cần biết mọi thứ về con voi (đối tượng trò chơi) để có thể thực hiện chức năng của nó.

Kết hợp logic trò chơi và kết xuất thực sự giống như làm cho mọi người nhìn thấy toàn bộ con voi. Nếu có gì đó thay đổi mọi người cần biết về nó. Trong khi trong hầu hết các trường hợp, họ chỉ cần nhìn thấy phần mà họ chỉ quan tâm. Nếu có gì đó thay đổi người biết về nó, chỉ cần nói với người khác về kết quả của sự thay đổi đó, đó là điều quan trọng đối với anh ta (nghĩ về điều này như giao tiếp qua tin nhắn hoặc giao diện).

nhập mô tả hình ảnh ở đây

Những điểm bạn đề cập không phải là mặt sau, chúng chỉ là mặt sau nếu có nhiều phụ thuộc hơn mức cần có trong động cơ, nói cách khác, hệ thống nhìn thấy các bộ phận của con voi nhiều hơn mức cần thiết. Và điều này có nghĩa là động cơ không được thiết kế "chính xác".

Bạn chỉ cần đồng bộ hóa với định nghĩa chính thức nếu bạn đang sử dụng công cụ đa luồng trong đó nó đặt logic và kết xuất thành hai luồng khác nhau và thậm chí một công cụ cần nhiều đồng bộ hóa giữa các hệ thống không được thiết kế đặc biệt.

Mặt khác, cách tự nhiên để đối phó với trường hợp đó, là thiết kế hệ thống làm đầu vào / đầu ra. Bản cập nhật thực hiện logic và đưa ra kết quả. Kết xuất chỉ là nguồn cấp dữ liệu với kết quả từ bản cập nhật. Bạn không thực sự cần phải phơi bày tất cả mọi thứ. Bạn chỉ hiển thị một Giao diện giao tiếp giữa hai giai đoạn. Giao tiếp giữa các bộ phận khác nhau của động cơ nên thông qua trừu tượng (giao diện) và / hoặc tin nhắn. Không có logic hoặc trạng thái nội bộ nên được tiếp xúc.

Hãy lấy một ví dụ biểu đồ cảnh đơn giản để giải thích ý tưởng.

Việc cập nhật thường được thực hiện thông qua một vòng lặp duy nhất được gọi là vòng lặp trò chơi (hoặc có thể thông qua nhiều vòng lặp trò chơi, mỗi vòng chạy trong một luồng riêng biệt). Khi vòng lặp cập nhật từng đối tượng trò chơi. Nó chỉ cần thông báo qua tin nhắn hoặc giao diện mà các đối tượng 1 và 2 đã cập nhật và cung cấp cho nó với biến đổi cuối cùng.

Hệ thống kết xuất chỉ thực hiện chuyển đổi cuối cùng và không biết điều gì thực sự thay đổi về đối tượng (ví dụ như va chạm cụ thể đã xảy ra, v.v.). Bây giờ để hiển thị đối tượng đó, nó chỉ cần ID của đối tượng đó và biến đổi cuối cùng. Sau đó, trình kết xuất sẽ cung cấp api kết xuất với lưới và biến đổi cuối cùng mà không biết gì khác.

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.