Tôi có đang đi đúng hướng với kiến ​​trúc thành phần này không?


9

Gần đây tôi đã quyết định cải tổ kiến ​​trúc trò chơi của mình để thoát khỏi hệ thống phân cấp lớp sâu và thay thế chúng bằng các thành phần có thể định cấu hình. Hệ thống phân cấp đầu tiên tôi thay thế là hệ thống phân cấp Mục và tôi muốn một số lời khuyên để biết liệu tôi có đang đi đúng hướng hay không.

Trước đây, tôi có một hệ thống phân cấp giống như thế này:

Item -> Equipment -> Weapon
                  -> Armor
                  -> Accessory
     -> SyntehsisItem
     -> BattleUseItem -> HealingItem
                      -> ThrowingItem -> ThrowsAsAttackItem

Không cần phải nói rằng nó đã bắt đầu trở nên lộn xộn và đây không phải là giải pháp dễ dàng cho các vật phẩm cần có nhiều loại (tức là một số thiết bị được sử dụng trong tổng hợp vật phẩm, một số thiết bị có thể ném được, v.v.)

Sau đó tôi đã cố gắng cấu trúc lại và đặt chức năng vào lớp mục cơ sở. Nhưng sau đó tôi đã lưu ý rằng Mục có rất nhiều dữ liệu không được sử dụng / không cần thiết. Bây giờ tôi đang cố gắng thực hiện một thành phần như kiến ​​trúc, ít nhất là cho các mục của tôi trước khi thử làm điều đó với các lớp trò chơi khác của tôi.

Đây là những gì tôi hiện đang nghĩ cho việc thiết lập thành phần:

Tôi có một lớp vật phẩm cơ sở có các khe cho các thành phần khác nhau (ví dụ: khe thành phần thiết bị, khe thành phần chữa bệnh, v.v. cũng như bản đồ cho các thành phần tùy ý) nên đại loại như thế này:

class Item
{
    //Basic item properties (name, ID, etc.) excluded
    EquipmentComponent* equipmentComponent;
    HealingComponent* healingComponent;
    SynthesisComponent* synthesisComponent;
    ThrowComponent* throwComponent;
    boost::unordered_map<std::string, std::pair<bool, ItemComponent*> > AdditionalComponents;
} 

Tất cả các thành phần vật phẩm sẽ kế thừa từ một lớp ItemComponent cơ sở và mỗi loại Thành phần chịu trách nhiệm cho động cơ biết cách thực hiện chức năng đó. tức là HealingComponent cho các thợ máy chiến đấu biết cách tiêu thụ vật phẩm này như một vật phẩm hồi máu, trong khi throwComponent nói với cỗ máy chiến đấu cách coi vật phẩm đó là một vật phẩm có thể ném được.

Bản đồ được sử dụng để lưu trữ các thành phần tùy ý không phải là thành phần mục chính. Tôi đang ghép nối nó với một bool để cho biết liệu Container Container có nên quản lý ItemComponent hay không nếu nó được quản lý bởi một nguồn bên ngoài.

Ý tưởng của tôi ở đây là tôi xác định các thành phần cốt lõi được sử dụng bởi công cụ trò chơi của mình và nhà máy sản xuất vật phẩm của tôi sẽ chỉ định các thành phần mà vật phẩm thực sự có, nếu không chúng là null. Bản đồ sẽ chứa các thành phần tùy ý thường được thêm / tiêu thụ bởi các tập lệnh.

Câu hỏi của tôi là, đây có phải là một thiết kế tốt? Nếu không, làm thế nào nó có thể được cải thiện? Tôi đã xem xét việc nhóm tất cả các thành phần vào bản đồ, nhưng sử dụng lập chỉ mục chuỗi dường như không cần thiết cho các thành phần mục cốt lõi

Câu trả lời:


8

Có vẻ như một bước đầu tiên rất hợp lý.

Bạn đang chọn kết hợp tổng quát (bản đồ "các thành phần bổ sung") và hiệu suất tra cứu (các thành viên được mã hóa cứng), có thể là một chút tối ưu hóa trước - quan điểm của bạn về tính không hiệu quả chung của dựa trên chuỗi tra cứu được thực hiện tốt, nhưng bạn có thể làm giảm bớt điều đó bằng cách chọn lập chỉ mục các thành phần bằng cách nhanh hơn để băm. Một cách tiếp cận có thể là cung cấp cho mỗi loại thành phần một ID loại duy nhất (về cơ bản bạn đang triển khai RTTI tùy chỉnh nhẹ ) và chỉ mục dựa trên đó.

Bất kể, tôi sẽ cảnh báo bạn để lộ API công khai cho đối tượng Item cho phép bạn yêu cầu bất kỳ thành phần nào - các thành phần được mã hóa cứng và các thành phần bổ sung - theo cách thống nhất. Điều này sẽ giúp dễ dàng thay đổi biểu diễn hoặc cân bằng cơ bản của các thành phần được mã hóa cứng / không mã hóa mà không phải cấu trúc lại tất cả các máy khách thành phần vật phẩm.

Bạn cũng có thể xem xét việc cung cấp các phiên bản no-op "giả" của từng thành phần được mã hóa cứng và đảm bảo rằng chúng luôn được gán - sau đó bạn có thể sử dụng các thành viên tham chiếu thay vì con trỏ và bạn sẽ không bao giờ cần phải kiểm tra con trỏ NULL trước khi tương tác với một trong các lớp thành phần được mã hóa cứng. Bạn vẫn sẽ phải trả chi phí cho công văn động để tương tác với các thành viên của thành phần đó, nhưng điều đó sẽ xảy ra ngay cả với các thành viên con trỏ. Đây là nhiều hơn một vấn đề về độ sạch mã bởi vì tác động hiệu suất sẽ không đáng kể trong tất cả các khả năng.

Tôi không nghĩ rằng đó là một ý tưởng tuyệt vời để có hai loại phạm vi trọn đời khác nhau (nói cách khác, tôi không nghĩ rằng bool bạn có trong bản đồ thành phần bổ sung là một ý tưởng tuyệt vời). Nó làm phức tạp hệ thống và ngụ ý rằng sự phá hủy và giải phóng tài nguyên sẽ không mang tính quyết định khủng khiếp. API cho các thành phần của bạn sẽ rõ ràng hơn nhiều nếu bạn chọn một chiến lược quản lý trọn đời hoặc chiến lược khác - thực thể quản lý vòng đời thành phần hoặc hệ thống con nhận ra các thành phần (tôi thích cái sau hơn vì nó kết hợp tốt hơn với thành phần bên ngoài cách tiếp cận, mà tôi sẽ thảo luận tiếp theo).

Nhược điểm lớn mà tôi thấy với cách tiếp cận của bạn là bạn đang kết hợp tất cả các thành phần lại với nhau trong đối tượng "thực thể", thực sự không phải lúc nào cũng là thiết kế tốt nhất. Từ câu trả lời liên quan của tôi đến một câu hỏi dựa trên thành phần khác:

Cách tiếp cận của bạn về việc sử dụng bản đồ lớn các thành phần và lệnh gọi () trong đối tượng trò chơi là khá tối ưu (và một cạm bẫy chung cho những người đầu tiên xây dựng các loại hệ thống này). Nó tạo ra sự đồng bộ bộ đệm rất kém trong quá trình cập nhật và không cho phép bạn tận dụng lợi thế đồng thời và xu hướng xử lý theo kiểu SIMD của các lô dữ liệu hoặc hành vi lớn cùng một lúc. Tốt hơn là nên sử dụng một thiết kế trong đó đối tượng trò chơi không cập nhật các thành phần của nó, mà là hệ thống con chịu trách nhiệm cho chính thành phần đó cập nhật tất cả chúng cùng một lúc.

Về cơ bản, bạn đang thực hiện cùng một cách tiếp cận bằng cách lưu trữ các thành phần trong thực thể vật phẩm (một lần nữa, đây là bước đầu tiên hoàn toàn chấp nhận được). Những gì bạn có thể khám phá là phần lớn quyền truy cập vào các thành phần bạn quan tâm về hiệu suất của nó chỉ là để cập nhật những thành phần đó và nếu bạn chọn sử dụng một cách tiếp cận bên ngoài hơn cho tổ chức thành phần, trong đó các thành phần được giữ trong một bộ đệm kết hợp , cấu trúc dữ liệu hiệu quả (cho miền của họ) bởi một hệ thống con hiểu rõ nhu cầu của họ nhất, bạn có thể đạt được hiệu suất cập nhật tốt hơn, song song hơn nhiều.

Nhưng tôi chỉ nêu ra điều này như là một điều gì đó được coi là một hướng đi trong tương lai - bạn chắc chắn không muốn vượt qua điều này; bạn có thể thực hiện chuyển đổi dần dần thông qua tái cấu trúc liên tục hoặc bạn có thể khám phá việc triển khai hiện tại của mình đáp ứng hoàn hảo nhu cầu của bạn và không cần phải lặp lại trên đó.


1
+1 để đề xuất loại bỏ đối tượng Item. Mặc dù nó sẽ được làm việc nhiều hơn về phía trước, nhưng cuối cùng nó sẽ tạo ra một hệ thống thành phần tốt hơn.
James

Tôi đã có thêm một vài câu hỏi, không chắc là tôi có nên bắt đầu topiuc mới hay không, vì vậy trước tiên hãy thử dưới đây: Đối với lớp vật phẩm của tôi, không có phương pháp nào tôi sẽ gọi là chuyển khung hình (hoặc thậm chí đóng). Đối với các hệ thống con đồ họa của tôi, tôi sẽ lấy lời khuyên của bạn và giữ tất cả các đối tượng để cập nhật trong hệ thống. Câu hỏi khác tôi có là làm thế nào để tôi xử lý kiểm tra thành phần? Ví dụ, tôi muốn xem liệu tôi có thể sử dụng một mục như X hay không, vì vậy, tự nhiên tôi sẽ kiểm tra xem liệu mục đó có thành phần cần thiết để thực hiện X. Đây có phải là cách đúng đắn để làm điều này? Cảm ơn một lần nữa cho câu trả lời
127817
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.