Xin chào, Hackeroon! Chủ đề tiếp theo tôi chọn là ECS (Entity-Component-System) trong . Tôi đã chia nó thành hai phần để giúp bạn tiếp nhận tất cả các thông tin dễ tiếp cận hơn. phát triển Unity Tôi sẽ kể cho bạn mọi thứ tôi biết về Thực thể-Thành phần-Hệ thống và cố gắng xua tan những định kiến khác nhau về cách tiếp cận này. Bạn sẽ tìm thấy nhiều từ về ưu điểm và nhược điểm của ECS, đặc thù của phương pháp này, cách kết bạn với nó, những cạm bẫy tiềm ẩn và các phương pháp hữu ích. Tôi cũng sẽ xem xét ngắn gọn các khung ECS cho Unity/C#. Bài viết này sẽ rất hữu ích cho những ai muốn/bắt đầu làm quen với ECS. Tôi hy vọng những người đã nếm thử ECS cũng sẽ có thể nhấn mạnh điều gì đó mới mẻ cho bản thân. Nếu bạn tạo trò chơi bằng bất kỳ ngôn ngữ nào khác ngoài C#, bạn vẫn có thể thấy bài viết này hữu ích. Sẽ không có mẫu mã và lịch sử của mẫu, chỉ có kinh nghiệm, lý luận và quan sát của tôi :) ECS (Thực thể-Thành phần-Hệ thống) là gì? Thực thể-Thành phần-Hệ thống là một mẫu kiến trúc được tạo riêng cho phát triển trò chơi. Nó là hoàn hảo để mô tả một thế giới ảo năng động. Vì những đặc thù của nó, một số người coi nó gần như là một mô hình lập trình mới. ECS là một nguyên tắc tuyệt đối của Thành phần hơn Kế thừa. Nó có thể là một ví dụ cụ thể về Thiết kế hướng dữ liệu (DOD), nhưng nó phụ thuộc vào việc diễn giải mẫu bằng cách triển khai cụ thể. Hãy giải mã tên của mẫu này: - một đối tượng trừu tượng tối đa. Nó là vùng chứa có điều kiện cho các thuộc tính xác định Thực thể này sẽ là gì. Nó thường được biểu diễn dưới dạng mã định danh để truy cập dữ liệu. Thực thể - thuộc tính có dữ liệu đối tượng. Các thành phần trong ECS chỉ được chứa dữ liệu thuần túy mà không có một chút logic nào. Tuy nhiên, một số nhà phát triển cho phép nhiều getters và setters khác nhau trong các thành phần. Tuy nhiên, tôi nghĩ rằng các tiện ích tĩnh phù hợp hơn cho những mục đích này. Thành phần - logic xử lý dữ liệu. Các hệ thống trong ECS không được chứa bất kỳ dữ liệu nào, chỉ có logic xử lý dữ liệu. Tuy nhiên, một lần nữa, một số nhà phát triển cho phép nó xác định một số hành vi phụ trợ của hệ thống, chẳng hạn như hằng số hoặc các dịch vụ phụ trợ khác nhau. Hệ thống Như bạn đã nhận ra từ phần trên: ECS tách dữ liệu khỏi logic một cách nghiêm ngặt. Hành vi của một đối tượng được xác định không phải bởi các giao diện/hợp đồng/API công khai, như chúng ta đã quen trong lập trình hướng đối tượng cổ điển (OOP), mà bởi các thuộc tính được gán cho đối tượng với dữ liệu + logic xử lý tồn tại riêng biệt. Trong ECS, dữ liệu xác định mọi thứ. Đây là thuộc tính chính phân biệt nó với các phương pháp phát triển khác: mọi thứ đều là dữ liệu. Các thuộc tính, đặc điểm và sự kiện của đối tượng chỉ là dữ liệu trong thế giới ECS. Logic chỉ đơn giản là quá trình xử lý đường ống của tất cả dữ liệu này. Tại sao cần có ECS? Chắc hẳn bạn đã có câu hỏi: "Tại sao tôi cần ECS? Nó có công dụng gì?". Và để giúp bạn quyết định có nên đọc thêm bài viết này hay không, tôi sẽ cho bạn biết lý do tại sao tôi thích ECS. Cá nhân tôi yêu thích ECS vì: Với ECS, bạn chỉ cần ngồi xuống và thay vì chiến đấu với kiến trúc của dự án. Không cần phải xây dựng các hệ thống phân cấp lớn và đẹp, nghĩ về nhiều kết nối và lo lắng về việc "X không nên biết về Y." Đồng thời, các nguyên tắc của ECS bảo vệ bạn (tất nhiên không phải 100%) khỏi tình huống vô vọng do kiến trúc xấu gây ra khi việc phát triển dự án tiếp theo trở nên rất khó khăn. , theo tôi, là điều tốt nhất về ECS. tạo một trò chơi trong Unity Mã trên ECS đơn giản và rõ ràng. Bạn không cần phải thu thập thông tin qua các cuộc gọi giữa các lớp để hiểu chức năng của một hệ thống cụ thể. Bạn có thể thấy mọi thứ cùng một lúc, đặc biệt nếu bạn chia một tính năng thành các hệ thống, hệ thống thành các phương thức và không phức tạp hóa mã. Ngoài ra, ECS đơn giản hóa rất nhiều việc lập hồ sơ. Bạn có thể thấy ngay logic (hệ thống) nào chiếm bao nhiêu thời gian của khung hình. Bạn không cần phải tìm kiếm nguồn gốc của sự chậm trễ trong độ sâu của các cuộc gọi. Thật dễ dàng để thao tác logic. Thêm logic mới thực tế không gây đau đớn. Bạn chỉ cần chèn một hệ thống mới vào đúng vị trí mà không sợ ảnh hưởng trực tiếp đến phần còn lại của mã (cần lưu ý rằng có thể ảnh hưởng gián tiếp thông qua dữ liệu). Bạn có thể sử dụng logic (hệ thống) chung giữa máy khách và máy chủ mà không gặp bất kỳ sự cố nào trong khi vẫn giữ dữ liệu (thành phần) được sử dụng. Bạn có thể dễ dàng viết lại hệ thống, thay thế hệ thống cũ bằng hệ thống được tái cấu trúc mà không ảnh hưởng đến phần còn lại của mã. Nếu bạn không thích kết quả, chỉ cần bật lại hệ thống cũ. Cơ chế tương tự có thể dễ dàng tổ chức các thử nghiệm A/B. Mọi thứ đều xoay quanh dữ liệu. Nó hóa ra là cực kỳ thuận tiện. Bằng cách thao tác trực tiếp dữ liệu trên các thực thể, khả năng tổ hợp là rất lớn. Bạn có thể sử dụng dữ liệu để nhào nặn một thực thể thành bất kỳ thứ gì. Và giả sử khung cung cấp các công cụ để xem dữ liệu về các thực thể. Trong trường hợp đó, bạn có thể kiểm tra dữ liệu và tính năng động của dữ liệu trên bất kỳ thực thể nào mà không cần chạy trình gỡ lỗi để tìm kiếm trong bộ nhớ. Bây giờ bạn đã hiểu tôi chưa? Làm thế nào để làm việc với ECS? Ở đây tôi sẽ mô tả bằng những từ đơn giản cách thức hoạt động của quy trình phát triển với ECS trong một ví dụ đơn giản nhất. Tôi sẽ làm điều này một cách trừu tượng nhất có thể mà không cần tham khảo ngôn ngữ lập trình. Nếu bạn đã có một số kinh nghiệm với ECS, bạn có thể chuyển thẳng sang phần tiếp theo :) tạo đối tượng chuyển động theo hướng của vectơ chuyển động cho trước. Nhiệm vụ: Trước tiên, hãy xác định dữ liệu chúng tôi cần cho công việc của mình. Đối với nhiệm vụ của chúng tôi, chúng tôi sẽ cần vị trí của đối tượng và vectơ chuyển động đã cho. Trong ngôn ngữ ECS, đây sẽ là: PositionComponent để lưu trữ vectơ vị trí MovementComponent cho vectơ chuyển động Bước tiếp theo là mô tả logic. Hãy tạo một . Trong phương thức chính của hệ thống, tùy thuộc vào việc triển khai, nó có thể là hoặc một cái gì đó khác. Bạn nhận được tất cả các thực thể trong ECS có và . Việc này có thể được thực hiện chính xác như thế nào tùy thuộc vào khung, nhưng thường thì nó trông giống như một loại truy vấn SQL như . MovementSystem Run()/Execute()/Update() PositionComponent MovementComponent GetAllEntities().With<PositionComponent>().With<MovementComponent>() Và cuối cùng, bạn chỉ cần tạo một thực thể (thậm chí mười phần) với hai thành phần của chúng ta và đặt vectơ chuyển động khác 0. Bây giờ, tại mỗi lần gọi (bất kể chúng ta gọi nó ở đâu và khi nào), đối tượng của chúng ta sẽ thay đổi vị trí theo hướng của vectơ chuyển động đã cho. Nhiệm vụ hoàn thành! :) MovementSystem Thông thường, các hệ thống bằng cách nào đó được nhúng vào GameLoop của dự án và tự động giật mọi khung hình bằng chính công cụ đó. Nhưng bạn có thể làm điều đó bằng tay và bất kỳ cách nào khác, bởi vì nó chỉ là một cuộc gọi phương thức. Hãy xem chúng ta có thêm những khả năng phát triển nào ngoài việc giải quyết vấn đề chính: Bất kỳ hệ thống nào khác của chúng tôi đều có thể xác định xem một đối tượng có đang di chuyển hay không bằng cách kiểm tra sự hiện diện của thuộc tính MovementComponent Bất kỳ hệ thống nào khác có thể lấy vectơ chuyển động cho nhu cầu của nó Bất kỳ hệ thống nào khác của chúng tôi sẽ có thể chỉ định một vectơ chuyển động cho bất kỳ thực thể nào của chúng tôi theo ý muốn Nếu muốn, chúng ta cũng có thể làm cho bất kỳ thực thể nào khác di chuyển bằng cách đặt và trên đó. Điều này rất hữu ích khi . PositionComponent MovementComponent tạo game Unity Ưu điểm của ECS trong Unity Trong phần này, chúng ta sẽ thảo luận về điểm tốt và điểm chưa tốt của ECS. Một số tính năng được mô tả dưới đây có hai mặt của đồng tiền. Chúng vừa có lợi cho sự phát triển vừa không thoải mái, tạo ra những hạn chế mà đôi khi phải vượt qua. Đầu tiên, hãy thảo luận về những ưu điểm của ECS trong Unity. Sự gắn kết mã yếu Đây là một tài sản có lợi cho . Nó cho phép chúng tôi tái cấu trúc và mở rộng cơ sở mã tương đối dễ dàng và không phá vỡ các đoạn mã cũ. Chúng tôi luôn có thể thêm hành vi mới bằng cách sử dụng dữ liệu cũ ở bên cạnh mà không can thiệp vào logic cũ theo bất kỳ cách nào. ECS đạt được hiệu ứng này vì dữ liệu thể hiện tất cả các tương tác logic trong Thực thể. Đây là một đối tượng trừu tượng tối đa mà không có bất kỳ sự đảm bảo nào, giống như một số Đối tượng trong C#/Java. các nhà phát triển trò chơi Unity Tuy nhiên, bạn nên nhớ rằng trong ECS, thứ tự thay đổi dữ liệu đóng một vai trò quan trọng. Cuối cùng, nó có thể ảnh hưởng đến sự phức tạp của việc tái cấu trúc và phá vỡ logic cũ của bạn hoặc thậm chí tạo ra các lỗi tác dụng phụ khó chịu. Mô-đun hoàn hảo và khả năng kiểm tra logic Nếu tất cả tương tác được thể hiện bằng dữ liệu thuần túy, logic của chúng tôi luôn được tách rời hoàn toàn khỏi nguồn dữ liệu. Điều này cho phép chúng tôi di chuyển logic từ dự án này sang dự án khác và sử dụng lại nó (tất nhiên là trong khi vẫn giữ nguyên định dạng dữ liệu), cũng như chạy logic trên bất kỳ dữ liệu đầu vào nào để kiểm tra hoạt động của nó. Viết mã kém khó hơn ECS ít đòi hỏi hơn về kiến trúc bởi vì nó thiết lập khuôn khổ mà với đó sẽ khó tạo ra một thiết kế mã thực sự tồi tệ hơn. Đồng thời, như đã nói ở trên, chúng ta có thể khắc phục sự cố tương đối dễ dàng và ít tác động đến phần còn lại của mã, ngay cả khi thiết kế mã xấu xảy ra. ECS cho phép chúng tôi ít phải suy nghĩ hơn về việc "làm thế nào để khớp logic này vào kiến trúc của chúng tôi mà không vi phạm bất cứ điều gì" và thêm các tính năng mới. tổ hợp tài sản Ưu điểm này làm cho ECS trở thành một lựa chọn tuyệt vời để mô tả thế giới động. Chỉ cần tưởng tượng: bạn có thể cung cấp bất kỳ thuộc tính nào (và do đó logic) cho bất kỳ thực thể nào của bạn mà không gặp bất kỳ rắc rối nào! Nếu bạn muốn máy ảnh hoạt động tốt, bạn có thể đặt trên máy ảnh. Nó sẽ chịu thiệt hại (nếu có một hệ thống như vậy). Đặt một trên một thực thể và nó ngay lập tức bắt đầu nhận sát thương do cháy nếu nó có . Bạn muốn ngôi nhà di chuyển dưới sự điều khiển của người chơi? Không thành vấn đề, chỉ cần sử dụng . HealthComponent InFireComponent HealthComponent PlayerInputListenerComponent Một nhà phát triển có kinh nghiệm sẽ nói: "Hah, hầu hết các mẫu Thành phần trên Kế thừa đều có thể xử lý việc này. ECS tốt hơn như thế nào?". Câu trả lời của tôi là: "ECS cho phép bạn kết hợp các thuộc tính không chỉ về mặt hình thành thực thể mà còn tạo logic cụ thể khi kết hợp nhiều thuộc tính (thành phần) trên cùng một thực thể." Tôi thậm chí còn chưa đề cập đến khả năng thêm logic hoàn toàn mới cho dữ liệu cũ mà không cần chạm vào các thành phần của thực thể! Việc thực thi một Trách nhiệm duy nhất sẽ dễ dàng hơn Khi chúng ta có logic hoàn toàn tách biệt với dữ liệu và không bị ràng buộc với bất kỳ đối tượng/thực thể nào, việc kiểm soát phân vùng logic theo mục đích của nó sẽ trở nên dễ dàng hơn thay vì vị trí của nó trong hệ thống phân cấp. Mỗi hệ thống chỉ thực hiện một số nhiệm vụ cụ thể duy nhất cho nó. Thường thì mã hệ thống trông giống như một lệnh gọi phương thức duy nhất cho nhiều thành phần cùng loại. Kết quả là, mã hầu hết đều dễ đọc và dễ hiểu. Hồ sơ rõ ràng hơn Khi lập hồ sơ, chúng ta có thể xem logic nào và thời lượng khung hình cần thiết. Điều này có thể thực hiện được nhờ các hệ thống riêng biệt với logic chịu trách nhiệm xử lý. Chúng ta không cần đi sâu vào ngăn xếp cuộc gọi để hiểu điều gì chiếm nhiều thời gian nhất. Chúng ta có thể thấy ngay CharMovementSystem có lỗi. Cần lưu ý rằng lợi thế này phụ thuộc vào thiết bị khung ECS vì bản thân khung có thể có ngăn xếp lệnh gọi của nó. ECS có thể tăng hiệu suất tốt Nhiều người nghĩ rằng hiệu suất tốt là lợi thế chính của ECS (nhờ tuyên truyền Unity). Điều này hoàn toàn không đúng. Tốc độ thực thi mã chỉ là một phần thưởng tuyệt vời do các nguyên tắc của mẫu: dữ liệu ở một nơi - logic ở nơi khác + SIMD (một lệnh, nhiều dữ liệu). Và nếu khung tuân theo DOD khi triển khai ECS và đạt được vị trí dữ liệu tốt, thì chúng tôi cũng nhận được mã thân thiện với bộ đệm hơn, điều này sẽ khiến bộ xử lý của bạn hài lòng. Hiệu suất cuối cùng của ECS phụ thuộc vào nhiều yếu tố: khung lưu trữ dữ liệu chính xác như thế nào, khung lọc các thực thể như thế nào, hệ thống truy cập dữ liệu nhanh như thế nào và mã bên trong hệ thống của bạn hoạt động nhanh như thế nào. Tuy nhiên, , ECS sẽ luôn nhanh hơn phương pháp MonoBehaviour thông thường, đặc biệt là trên lượng dữ liệu lớn. Nhưng đừng quên rằng điều quan trọng đối với hiệu suất trò chơi của bạn không phải là mẫu kiến trúc mà là độ phức tạp thuật toán và hiệu suất của mã bạn viết. trong bối cảnh phát triển Unity Song song hóa xử lý dữ liệu dễ dàng hơn Vì logic được tách thành một bộ xử lý dữ liệu riêng biệt và dữ liệu thực sự là một chuỗi tuyến tính, nên chúng tôi có thể xử lý song song trong một hệ thống mà không gặp bất kỳ sự cố nào. Điều này rất quan trọng nếu hệ thống xử lý đồng thời một số lượng lớn các thực thể và chúng không giao nhau với nhau theo bất kỳ cách nào. Bạn có thể đi xa hơn nữa và gửi đến các luồng logic khác nhau không trùng lặp với dữ liệu đã thay đổi. Tuy nhiên, nó khó kiểm soát và giám sát hơn rất nhiều. Tuy nhiên, sẽ có một nút cổ chai trong quá trình đồng bộ hóa với luồng chính để chuẩn bị dữ liệu. Ngoài ra, có thể chi phí chung cho việc chuẩn bị và phân phối dữ liệu giữa các luồng sẽ cao hơn thời gian thực thi mã trong hệ thống của bạn. Do đó, bạn cần đánh giá xem nó có đáng không. Dữ liệu sạch rất dễ làm việc với Trong hầu hết mọi trò chơi Unity, chúng tôi phải lưu, tải hoặc sắp xếp thứ tự thứ gì đó để gửi qua mạng. Điều này dễ dàng hơn nhiều khi dữ liệu được tách ra khỏi logic. Không cần phải suy nghĩ, "Làm cách nào để điều này xâm nhập vào dữ liệu riêng tư..." và gọi một số phương thức đặc biệt để tuần tự hóa thích hợp. Bạn chỉ cần lưu/tải các thành phần cần thiết trên thực thể. Sau đó, hệ thống sẽ hoàn thành nó ở trạng thái mong muốn nếu xét thấy cần thiết. Bạn có thể thay đổi khung ECS thường xuyên như bạn muốn Các khung ECS tương tự nhau vì các nguyên tắc đều giống nhau. Một nhà phát triển đã xây dựng lại bộ não của mình cho ECS và đã hiểu rõ về một khung có thể làm việc với một khung ECS khác mà không gặp bất kỳ sự cố nào. Học API và các đặc thù của một khung cụ thể sẽ chỉ mất thời gian. Nhưng sẽ không cần phải xây dựng lại cái đầu của bạn cho cách tiếp cận mới. Nhược điểm của ECS trong Unity Như bạn có thể thấy, ECS trong Unity có nhiều lợi thế đáng giá so với các mẫu khác. Bây giờ hãy thảo luận về những nhược điểm của ECS trong Unity. Một ngưỡng cao cho các nhà phát triển Unity có kinh nghiệm Mặc dù khái niệm ECS có thể được mô tả trong một câu, nhưng việc học cách sử dụng nó một cách chính xác có thể cần thực hành nhiều. ECS yêu cầu bạn quên đi mọi thứ bạn đã biết về thiết kế trước đây: tất cả các hệ thống phân cấp thừa kế theo chiều dọc của bạn, rằng hành vi của một đối tượng được xác định bởi giao diện của nó, rằng một đối tượng là một thứ gì đó cụ thể và bất biến, rằng một đối tượng có thể có một không gian riêng tư và logic đó có thể được gọi bất cứ nơi nào bạn muốn. Trong ECS, mọi thứ không như vậy. Nó trái ngược với những gì được mô tả ở trên. Ở đây tất cả dữ liệu đều mở, tất cả các thực thể đều trừu tượng và rất năng động, các thuộc tính của chúng nằm trong một mặt phẳng và mọi người đều có thể truy cập được, logic hoạt động theo nguyên tắc băng tải và hành vi của các thực thể nói chung thay đổi nhanh chóng dựa trên dữ liệu. Sự gắn kết mã yếu có thể là một vấn đề Giả sử bạn đột nhiên cần sự tương tác chặt chẽ giữa 2 thực thể cụ thể (ví dụ: thân sâu bướm và tháp pháo xe tăng). Trong trường hợp đó, bạn gặp phải vấn đề là các thực thể là trừu tượng và bạn không thể đảm bảo ở cấp độ trình biên dịch rằng phần thân sâu bướm sẽ ở đầu kia. Điều này sẽ cản trở vì game Unity là nơi có nhiều tương tác chặt chẽ và bạn luôn muốn có một tài liệu tham khảo trực tiếp với sự đảm bảo về thuộc tính và hành vi. Bạn sẽ phải kiểm tra sự hiện diện của thành phần và bằng cách nào đó xử lý sự vắng mặt của nó, truy cập thành phần từ thực thể để bắt đầu tương tác với nó, v.v. Truy cập mọi dữ liệu từ mọi nơi Thế giới ECS là một hộp mở gồm các thực thể có sẵn dữ liệu cho tất cả các thành phần. Giống như sự gắn kết mã yếu ở trên, đây vừa là ưu điểm vừa là nhược điểm của ECS. Một mặt, nó cực kỳ tiện lợi. Bạn không cần phải tìm cách bỏ qua khuôn khổ tự giới hạn được tạo ra trước đó trong quá trình thiết kế ("X không được biết về Y") và đưa dữ liệu đã ẩn trước đó ra công chúng để giải quyết một số vấn đề tức thời. Mặt khác, bất kỳ lập trình viên thiếu kinh nghiệm nào cũng sẽ cố gắng thay đổi dữ liệu từ nơi không nên có. Nhưng thông thường, làm việc theo nhóm liên quan đến việc tin tưởng vào công việc của người khác, vì vậy hãy tin tưởng nhưng hãy xác minh;) Các hệ thống hoạt động hoàn toàn theo dòng chảy, hết cái này đến cái khác Khi thực hiện đúng các nguyên tắc của ECS, bạn không nên gọi logic của một hệ thống bên trong một hệ thống khác. Các hệ thống hoàn toàn không nên nhận thức được sự tồn tại của nhau. Nếu không, nó sẽ gây ra sự gắn kết mã không cần thiết và có khả năng gây hại cho dự án của bạn. Tuy nhiên, giới hạn này có thể gây bất tiện và đôi khi sẽ dẫn đến nhiều cách giải quyết khác nhau không vi phạm các nguyên tắc của ECS. Nếu bạn vẫn cần gọi một số mã ở đây và ngay bây giờ, chỉ cần tạo một đối tượng thông thường với các phương thức và đặt nó vào một thành phần, đừng tự hành hạ mình. Không hoạt động tốt với logic đệ quy Nhược điểm này là hậu quả của cái trước. Do thiếu khả năng gọi mã hệ thống bên ngoài luồng và bất cứ nơi nào chúng tôi muốn, ECS khiến việc tạo mã đệ quy bên ngoài bất kỳ hệ thống cụ thể nào hầu như không thể. Như một giải pháp cho thiếu sót này (hay còn gọi là giải pháp thay thế để tuân thủ các nguyên tắc của ECS), tôi chỉ có thể đề xuất bạn tạo một cấu trúc/hệ thống chuyên biệt sẽ gọi một danh sách các hệ thống cụ thể trong một vòng lặp vô hạn miễn là đáp ứng một điều kiện cụ thể. Ý tôi là, miễn là có các thực thể có DoActionComponent. Nếu bạn có cách giải quyết thanh lịch hơn, tôi rất vui khi đọc về chúng trong các nhận xét :) Trình tự thực hiện hệ thống là rất quan trọng Trong ECS, điều quan trọng là phải hiểu và kiểm soát cách hệ thống thay đổi dữ liệu của bạn. Thường có thể bỏ sót tác động của một số hệ thống đối với dữ liệu mà chúng ta đang làm việc và kết thúc với nhiều tác dụng phụ ngoài ý muốn. Nhân tiện, chúng có thể phức tạp để theo dõi (đó là nhược điểm tiếp theo). Tuy nhiên, khi viết các hệ thống, thường có thể thiết kế chúng theo cách không quan trọng thứ tự các hệ thống được gọi. Khó gỡ lỗi hơn Đây là một điểm khá gây tranh cãi, đặc biệt là với các IDE thông minh hiện đại. Do thiếu StackTrace chuyên sâu (chúng tôi có logic trong hệ thống của mình không gắn với thực thể) và không thể theo dõi cách thức và người thay đổi trạng thái dữ liệu và thực thể, có thể khó tìm ra lý do tại sao hệ thống của bạn đột nhiên không hoạt động theo cách nó được dự định. Không dễ hiểu điều gì đã dẫn đến một cuộc gọi cụ thể, mặc dù ai đó vừa thêm một thành phần vào thực thể hoặc tạo thêm ++. Tóm lại, trong ECS, nếu không có các công cụ sửa lỗi, thật khó để theo dõi lý do và cách thức dữ liệu trong các thành phần thay đổi, đặc biệt là khi bạn có hàng nghìn thực thể và chỉ có một thực thể có vấn đề. Điều này có thể được khắc phục bằng các công cụ sửa lỗi mà các khung có thể cung cấp. Nhưng chúng có thể không có sẵn và bạn phải tự viết chúng hoặc chịu đựng. Tùy chọn khủng khiếp cho cấu trúc dữ liệu, đặc biệt là cấu trúc phân cấp Việc triển khai cấu trúc dữ liệu với ECS rất khó khăn, bất tiện và theo tôi là vô nghĩa. Tôi không nói là không thể (nếu bạn đủ cố gắng thì điều gì cũng có thể), nhưng đó sẽ là một con đường chông gai và không có nhiều lợi ích ở cuối con đường, vì vậy hãy lý trí trong lựa chọn của mình. Tôi sẽ liệt kê một số vấn đề sẽ cản trở khi cố gắng nhận ra một số cấu trúc dữ liệu trên ECS: Trong ECS, tất cả dữ liệu đều có thể truy cập được từ mọi nơi. Điều này có thể cực kỳ nguy hiểm đối với các cấu trúc dữ liệu yêu cầu tính nhất quán tối đa. Bất kỳ "con cá sấu" nào đi qua đều có thể thay đổi bất kỳ dữ liệu nội bộ nào để bỏ qua logic của bạn, phá vỡ hoàn toàn cấu trúc dữ liệu của bạn. Nếu chúng tôi trung thực tuân theo các nguyên tắc của ECS, thì chúng tôi không thể gọi logic của cấu trúc dữ liệu của mình ở đây và bây giờ, như thường được yêu cầu khi làm việc với chúng. Tuy nhiên, điểm này có thể được khắc phục bằng các tiện ích/tiện ích mở rộng tĩnh. ECS là đại diện của kiến trúc ngang. Tất cả dữ liệu trong đó nằm trong một mặt phẳng, hầu như luôn chỉ là mảng một chiều của các thành phần. Điều này gây khó khăn nếu cấu trúc dữ liệu của bạn yêu cầu theo chiều dọc/phân cấp. Không có gì lạ khi cấu trúc dữ liệu yêu cầu tham chiếu chéo giữa các phần tử (phân cấp). Tuy nhiên, như bạn có thể nhớ, mọi thứ đều xoay quanh một Thực thể trừu tượng tối đa trong ECS. Nó gây khó khăn khi làm việc vì không có gì đảm bảo về một phần tử thuộc loại mà chúng ta cần ở đầu bên kia. Kết quả là, nó sẽ phải được xử lý riêng. Cấu trúc dữ liệu và các phần tử của nó thường không cần thay đổi định dạng dữ liệu trong thời gian chạy, cũng như không cần tổ hợp. Chúng khá cứng nhắc. Mỗi thực thể cấu trúc dữ liệu cuối cùng có thể chỉ có một thành phần. Giả sử bạn vẫn cần một cấu trúc dữ liệu. Trong trường hợp đó, tôi khuyên bạn nên tạo nó như một đối tượng riêng biệt với các phương thức, sau đó đặt đối tượng này vào thành phần của bạn và chỉ làm việc với nó từ hệ thống như bình thường. Nhiều tệp và lớp hơn Trong , số lượng tệp trong một dự án tăng nhanh hơn so với trường hợp mã tương tự trong các phương pháp cổ điển. Ít nhất là vì thay vì 1 lớp với dữ liệu và logic, bạn có hai lớp: thành phần và hệ thống (bạn vẫn có thể ẩn chúng trong một tệp). Nhiều nhất, nếu bạn tạo tất cả các thành phần nguyên tử (1 thành phần - 1 trường), sẽ có rất, rất nhiều tệp ... phương pháp ECS mã soạn sẵn Hạn chế này phụ thuộc rất nhiều vào việc triển khai cụ thể khung ECS. Trong một số khuôn khổ, bạn phải viết rất nhiều mã kỹ thuật. Ở những người khác, nhà phát triển đã cố gắng tạo API đơn giản nhất có thể và giảm thiểu bản soạn sẵn. Tuy nhiên, nếu bạn so sánh nó với các cách tiếp cận khác, hầu như luôn có ít nhất một lượng nhỏ mã bổ sung mà bạn phải viết. Ý tôi là khai báo các thành phần, lấy một bộ lọc với các thành phần cần thiết, lấy các thực thể từ nó, lấy một thành phần từ một thực thể, v.v. kết luận nhỏ Đến đây là hết phần 1. Trong phần 2, tôi sẽ thảo luận về: Những sai lầm của tân binh trong ECS Thực hành tốt trong ECS Các khung làm việc với ECS trong Unity/C# Nếu bạn có bất kỳ câu hỏi nào, hãy để lại trong phần bình luận!