Đây là một trong những khó khăn. Tôi sẽ chỉ cố gắng giải quyết một số câu hỏi dựa trên kinh nghiệm cụ thể của tôi (YMMV):
Các thành phần phải truy cập dữ liệu của các thành phần khác. Ví dụ: phương thức vẽ của thành phần kết xuất phải truy cập vào vị trí của thành phần biến đổi. Điều này tạo ra sự phụ thuộc trong mã.
Đừng đánh giá thấp số lượng và độ phức tạp (không phải mức độ) của khớp nối / phụ thuộc ở đây. Bạn có thể xem xét sự khác biệt giữa điều này (và sơ đồ này đã được đơn giản hóa một cách lố bịch đến mức giống như đồ chơi và ví dụ trong thế giới thực sẽ có giao diện ở giữa để nới lỏng khớp nối):
... và điều này:
... Hoặc cái này:
Các thành phần có thể là đa hình mà tiếp tục giới thiệu một số phức tạp. Ví dụ: Có thể có một thành phần kết xuất sprite ghi đè phương thức vẽ ảo của thành phần kết xuất.
Vì thế? Tương tự (hoặc bằng chữ) tương đương với một công văn vtable và ảo có thể được gọi thông qua hệ thống thay vì đối tượng ẩn trạng thái / dữ liệu cơ bản của nó. Đa hình vẫn rất thực tế và khả thi với việc triển khai ECS "thuần túy" khi vtable hoặc con trỏ hàm tương tự biến thành "dữ liệu" các loại để hệ thống gọi.
Vì hành vi đa hình (ví dụ để kết xuất) phải được thực hiện ở đâu đó, nên nó chỉ được gia công vào các hệ thống. (ví dụ: hệ thống kết xuất sprite tạo nút kết xuất sprite kế thừa nút kết xuất và thêm nó vào công cụ kết xuất)
Vì thế? Tôi hy vọng điều này không xảy ra như một sự mỉa mai (không phải ý định của tôi mặc dù tôi đã bị buộc tội thường xuyên nhưng tôi ước tôi có thể giao tiếp cảm xúc tốt hơn qua văn bản), nhưng hành vi đa hình "gia công" trong trường hợp này không nhất thiết phải phát sinh thêm Chi phí đến năng suất.
Việc liên lạc giữa các hệ thống có thể khó tránh. Ví dụ, hệ thống va chạm có thể cần hộp giới hạn được tính từ bất kỳ thành phần kết xuất cụ thể nào.
Ví dụ này có vẻ đặc biệt kỳ lạ với tôi. Tôi không biết tại sao trình kết xuất sẽ xuất dữ liệu trở lại cảnh (tôi thường xem xét trình kết xuất chỉ đọc trong ngữ cảnh này) hoặc để trình kết xuất tìm ra AABB thay vì một số hệ thống khác thực hiện điều này cho cả trình kết xuất và va chạm / vật lý (tôi có thể bị treo lên tên "kết xuất thành phần" ở đây). Tuy nhiên, tôi không muốn quá bận tâm về ví dụ này vì tôi nhận ra đó không phải là điểm bạn đang cố gắng thực hiện. Tuy nhiên, việc liên lạc giữa các hệ thống (ngay cả ở dạng đọc / ghi gián tiếp vào cơ sở dữ liệu ECS trung tâm với các hệ thống phụ thuộc trực tiếp vào các biến đổi do người khác thực hiện) không cần phải thường xuyên, nếu cần thiết. Cái đó'
Điều này có thể dẫn đến các biểu tượng nếu thứ tự gọi các chức năng cập nhật của hệ thống không được xác định.
Điều này hoàn toàn nên được xác định. ECS không phải là giải pháp cuối cùng để sắp xếp lại thứ tự đánh giá xử lý hệ thống của mọi hệ thống có thể có trong cơ sở mã và lấy lại chính xác loại kết quả cho người dùng cuối xử lý các khung và FPS. Đây là một trong những điều, khi thiết kế ECS, ít nhất tôi nên đề xuất một cách mạnh mẽ nên được dự đoán trước một chút (mặc dù có rất nhiều phòng thở tha thứ để thay đổi suy nghĩ sau đó miễn là nó không làm thay đổi các khía cạnh quan trọng nhất của trật tự gọi / đánh giá hệ thống).
Tuy nhiên, tính toán lại toàn bộ tilemap mỗi khung hình là đắt tiền. Do đó, cần có một danh sách để theo dõi tất cả các thay đổi được thực hiện để sau đó cập nhật chúng trong hệ thống. Theo cách OOP, điều này có thể được gói gọn bởi thành phần bản đồ ô vuông. Ví dụ, phương thức SetTile () sẽ cập nhật mảng đỉnh bất cứ khi nào nó được gọi.
Tôi hoàn toàn không hiểu điều này ngoại trừ việc nó là mối quan tâm theo hướng dữ liệu. Và không có cạm bẫy nào trong việc đại diện và lưu trữ dữ liệu trong ECS, bao gồm ghi nhớ, để tránh những cạm bẫy hiệu suất đó (những lỗi lớn nhất với ECS có xu hướng liên quan đến những thứ như hệ thống truy vấn các trường hợp cụ thể có sẵn của một loại thành phần cụ thể là một trong những các khía cạnh thách thức nhất của việc tối ưu hóa một ECS tổng quát). Thực tế là logic và dữ liệu được phân tách trong một ECS "thuần túy" không có nghĩa là bạn đột nhiên phải tính toán lại những thứ mà bạn có thể đã lưu vào bộ nhớ cache / ghi nhớ trong một đại diện OOP. Đó là một điểm moot / không liên quan trừ khi tôi che đậy điều gì đó rất quan trọng.
Với ECS "thuần túy", bạn vẫn có thể lưu trữ dữ liệu này trong thành phần bản đồ ô vuông. Sự khác biệt lớn duy nhất là logic để cập nhật mảng đỉnh này sẽ di chuyển đến một hệ thống ở đâu đó.
Bạn thậm chí có thể dựa vào ECS để đơn giản hóa việc vô hiệu hóa và xóa bộ đệm này khỏi thực thể nếu bạn tạo một thành phần riêng như thế nào TileMapCache
. Tại thời điểm đó khi bộ đệm được mong muốn nhưng không có sẵn trong một thực thể có TileMap
thành phần, bạn có thể tính toán và thêm nó. Khi nó bị vô hiệu hoặc không còn cần thiết, bạn có thể xóa nó thông qua ECS mà không phải viết thêm mã cụ thể cho việc vô hiệu hóa và xóa đó.
Sự phụ thuộc giữa các thành phần vẫn tồn tại mặc dù bị ẩn đi trong các hệ thống
Không có sự phụ thuộc giữa các thành phần trong một đại diện "thuần túy" (Tôi không nghĩ hoàn toàn đúng khi nói rằng các phụ thuộc đang bị các hệ thống ẩn ở đây). Dữ liệu không phụ thuộc vào dữ liệu, có thể nói như vậy. Logic phụ thuộc vào logic. Và một ECS "thuần túy" có xu hướng thúc đẩy logic được viết theo cách phụ thuộc vào tập hợp con tối thiểu tuyệt đối của dữ liệu và logic (thường không có) mà một hệ thống yêu cầu hoạt động, không giống như nhiều lựa chọn thay thế thường khuyến khích nhiều chức năng hơn yêu cầu cho các nhiệm vụ thực tế. Nếu bạn đang sử dụng quyền ECS thuần túy, một trong những điều đầu tiên bạn nên đánh giá cao là lợi ích tách rời đồng thời đặt câu hỏi cho tất cả mọi thứ bạn từng học để đánh giá cao trong OOP về việc đóng gói và ẩn thông tin cụ thể.
Bằng cách tách rời, tôi đặc biệt có nghĩa là hệ thống của bạn cần ít thông tin như thế nào. Hệ thống chuyển động của bạn thậm chí không cần biết về một thứ phức tạp hơn nhiều như một Particle
hoặc Character
(nhà phát triển hệ thống thậm chí không nhất thiết phải biết những ý tưởng thực thể như vậy thậm chí tồn tại trong hệ thống). Nó chỉ cần biết về dữ liệu tối thiểu trần như một thành phần vị trí có thể đơn giản như một vài float trong một cấu trúc. Nó thậm chí còn ít thông tin hơn và ít phụ thuộc bên ngoài hơn so với những gì một giao diện thuần túy IMotion
có xu hướng mang theo cùng với nó. Chủ yếu là do kiến thức tối thiểu này mà mỗi hệ thống yêu cầu phải hoạt động khiến ECS thường tha thứ để xử lý các thay đổi thiết kế rất khó lường trong nhận thức mà không phải đối mặt với sự phá vỡ giao diện tầng ở mọi nơi.
Cách tiếp cận "không trong sạch" mà bạn đề xuất phần nào làm giảm lợi ích kể từ bây giờ logic của bạn không được định vị nghiêm ngặt cho các hệ thống mà các thay đổi không gây ra sự phá vỡ tầng. Logic bây giờ sẽ được tập trung ở một mức độ nào đó trong các thành phần được truy cập bởi nhiều hệ thống hiện phải đáp ứng các yêu cầu giao diện của tất cả các hệ thống khác nhau có thể sử dụng nó, và bây giờ nó giống như mọi hệ thống sau đó cần phải có kiến thức về (phụ thuộc vào) nhiều hơn thông tin hơn nó cần nghiêm túc để làm việc với thành phần đó.
Phụ thuộc vào dữ liệu
Một trong những điều gây tranh cãi về ECS là nó có xu hướng thay thế những gì có thể phụ thuộc vào các giao diện trừu tượng chỉ bằng dữ liệu thô và thường được coi là một hình thức ghép nối ít mong muốn và chặt chẽ hơn. Nhưng trong các loại lĩnh vực như trò chơi mà ECS có thể rất có lợi, việc thiết kế biểu diễn dữ liệu trước dễ dàng hơn và giữ ổn định hơn là thiết kế những gì bạn có thể làm với dữ liệu đó ở cấp trung tâm của hệ thống. Đó là điều mà tôi đã quan sát một cách đau đớn ngay cả trong số các cựu chiến binh dày dạn trong các cơ sở mã hóa sử dụng nhiều cách tiếp cận giao diện thuần túy theo phong cách COM với những thứ như thế IMotion
.
Các nhà phát triển tiếp tục tìm lý do để thêm, xóa hoặc thay đổi các chức năng cho giao diện trung tâm này và mỗi thay đổi đều rất khủng khiếp và tốn kém vì nó sẽ có xu hướng phá vỡ mọi lớp duy nhất được triển khai IMotion
cùng với mọi vị trí trong hệ thống được sử dụng IMotion
. Trong khi đó, toàn bộ thời gian với rất nhiều thay đổi đau đớn và xếp tầng, các đối tượng thực hiện IMotion
tất cả chỉ lưu trữ một ma trận nổi 4 x 4 và toàn bộ giao diện chỉ liên quan đến cách chuyển đổi và truy cập các phao đó; việc biểu diễn dữ liệu đã ổn định ngay từ đầu và rất nhiều nỗi đau có thể tránh được nếu giao diện tập trung này, do đó dễ bị thay đổi với nhu cầu thiết kế không dự đoán được, thậm chí không tồn tại ngay từ đầu.
Tất cả điều này có thể nghe có vẻ kinh tởm như các biến toàn cầu, nhưng bản chất của cách ECS tổ chức dữ liệu này thành các thành phần được truy xuất rõ ràng bằng cách thông qua các hệ thống khiến nó trở nên như vậy, trong khi trình biên dịch không thể thực thi bất cứ điều gì như ẩn thông tin, những nơi truy cập và biến đổi dữ liệu nói chung rất rõ ràng và đủ rõ ràng để vẫn duy trì hiệu quả các bất biến và dự đoán loại biến đổi và tác dụng phụ nào xảy ra từ hệ thống này sang hệ thống tiếp theo (thực sự theo những cách có thể đơn giản và dễ dự đoán hơn OOP trong một số lĩnh vực nhất định hệ thống biến thành một loại đường ống phẳng).
Cuối cùng, tôi muốn đặt câu hỏi về cách tôi sẽ xử lý hoạt hình trong một ECS thuần túy. Hiện tại tôi đã định nghĩa một hoạt hình là một functor thao túng một thực thể dựa trên một số tiến trình từ 0 đến 1. Thành phần hoạt hình có một danh sách các hoạt hình có một danh sách các hoạt hình. Trong chức năng cập nhật của nó, sau đó nó áp dụng bất kỳ hình ảnh động nào hiện đang hoạt động cho thực thể.
Chúng ta đều là những người thực dụng ở đây. Ngay cả trong gamedev, bạn có thể sẽ nhận được những ý tưởng / câu trả lời mâu thuẫn. Ngay cả ECS thuần túy nhất cũng là một hiện tượng tương đối mới, lãnh thổ tiên phong, mà mọi người không nhất thiết phải đưa ra ý kiến mạnh mẽ nhất về cách làm da mèo. Phản ứng ruột của tôi là một hệ thống hoạt hình làm tăng loại tiến trình hoạt hình này trong các thành phần hoạt hình để hệ thống kết xuất hiển thị, nhưng điều đó bỏ qua rất nhiều sắc thái cho ứng dụng và bối cảnh cụ thể.
Với ECS, đó không phải là viên đạn bạc và tôi vẫn thấy mình có xu hướng đi vào và thêm các hệ thống mới, loại bỏ một số, thêm các thành phần mới, thay đổi một hệ thống hiện có để chọn loại thành phần mới đó, v.v. mọi thứ ngay lần đầu tiên vẫn còn Nhưng sự khác biệt trong trường hợp của tôi là tôi không thay đổi bất cứ điều gì trung tâm khi tôi không lường trước được nhu cầu thiết kế nhất định. Tôi không nhận được hiệu ứng gợn sóng của các vụ vỡ tầng đòi hỏi tôi phải đi khắp nơi và thay đổi rất nhiều mã để xử lý một số nhu cầu mới tăng lên, và đó là cách tiết kiệm thời gian. Tôi cũng thấy nó dễ dàng hơn trong não vì khi tôi ngồi xuống với một hệ thống cụ thể, tôi không cần biết / nhớ nhiều về bất cứ thứ gì khác ngoài các thành phần có liên quan (chỉ là dữ liệu) để hoạt động trên nó.