Tôi đang thực hiện một biến thể hệ thống thực thể có:
Một lớp Thực thể ít hơn một ID liên kết các thành phần lại với nhau
Một nhóm các lớp thành phần không có "logic thành phần", chỉ có dữ liệu
Một nhóm các lớp hệ thống (còn gọi là "hệ thống con", "người quản lý"). Chúng làm tất cả các xử lý logic thực thể. Trong hầu hết các trường hợp cơ bản, các hệ thống chỉ lặp đi lặp lại một danh sách các thực thể mà chúng quan tâm và thực hiện một hành động đối với mỗi chúng
Một đối tượng lớp MessageChannel được chia sẻ bởi tất cả các hệ thống trò chơi. Mỗi hệ thống có thể đăng ký loại tin nhắn cụ thể để nghe và cũng có thể sử dụng kênh để truyền phát tin nhắn đến các hệ thống khác
Biến thể ban đầu của xử lý tin nhắn hệ thống là như thế này:
- Chạy một bản cập nhật trên mỗi hệ thống trò chơi một cách tuần tự
Nếu một hệ thống làm điều gì đó với một thành phần và hành động đó có thể được các hệ thống khác quan tâm, thì hệ thống sẽ gửi một thông điệp thích hợp (ví dụ: một cuộc gọi hệ thống
messageChannel.Broadcast(new EntityMovedMessage(entity, oldPosition, newPosition))
Bất cứ khi nào một thực thể được di chuyển)
Mỗi hệ thống đăng ký tin nhắn cụ thể được gọi là phương thức xử lý tin nhắn
Nếu một hệ thống đang xử lý một sự kiện và logic xử lý sự kiện yêu cầu một thông điệp khác được phát đi, thông báo sẽ được phát ngay lập tức và một chuỗi các phương thức xử lý tin nhắn khác được gọi
Biến thể này vẫn ổn cho đến khi tôi bắt đầu tối ưu hóa hệ thống phát hiện va chạm (nó đang trở nên rất chậm khi số lượng thực thể tăng lên). Lúc đầu, nó sẽ chỉ lặp lại mỗi cặp thực thể bằng thuật toán đơn giản. Sau đó, tôi đã thêm một "chỉ mục không gian" có một lưới các ô lưu trữ các thực thể nằm trong khu vực của một ô cụ thể, do đó chỉ cho phép kiểm tra các thực thể trong các ô lân cận.
Mỗi khi một thực thể di chuyển, hệ thống va chạm sẽ kiểm tra xem thực thể đó có va chạm với thứ gì đó ở vị trí mới hay không. Nếu có, một vụ va chạm được phát hiện. Và nếu cả hai thực thể va chạm là "vật thể" (cả hai đều có thành phần RigidBody và có nghĩa là đẩy nhau ra xa để không chiếm cùng một không gian), một hệ thống tách thân cứng chuyên dụng yêu cầu hệ thống chuyển động di chuyển các thực thể đến một số vị trí cụ thể sẽ tách chúng ra. Điều này đến lượt nó làm cho hệ thống chuyển động gửi tin nhắn thông báo về các vị trí thực thể đã thay đổi. Hệ thống phát hiện va chạm có nghĩa là phản ứng vì nó cần cập nhật chỉ số không gian của nó.
Trong một số trường hợp, nó gây ra sự cố do nội dung của ô (Danh sách các đối tượng Thực thể chung trong C #) bị sửa đổi trong khi chúng bị lặp đi lặp lại, do đó gây ra một ngoại lệ bị lặp bởi trình lặp.
Vậy ... làm thế nào tôi có thể ngăn hệ thống va chạm bị gián đoạn trong khi nó kiểm tra va chạm?
Tất nhiên tôi có thể thêm một số logic "thông minh" / "khó hiểu" để đảm bảo nội dung ô được lặp lại một cách chính xác, nhưng tôi nghĩ vấn đề không nằm ở chính hệ thống va chạm (tôi cũng gặp vấn đề tương tự trong các hệ thống khác), nhưng cách tin nhắn được xử lý khi chúng đi từ hệ thống này sang hệ thống khác. Những gì tôi cần là một số cách để đảm bảo rằng một phương thức xử lý sự kiện cụ thể sẽ thực hiện công việc mà không bị gián đoạn.
Những gì tôi đã thử:
- Hàng đợi tin nhắn đến . Mỗi khi một số hệ thống phát thông báo, tin nhắn sẽ được thêm vào hàng đợi tin nhắn của các hệ thống quan tâm đến nó. Các thông báo này được xử lý khi một bản cập nhật hệ thống được gọi là mỗi khung. Vấn đề : nếu hệ thống A thêm thông báo vào hàng B của hệ thống, nó sẽ hoạt động tốt nếu hệ thống B có nghĩa là được cập nhật muộn hơn hệ thống A (trong cùng khung trò chơi); mặt khác, nó làm cho thông báo xử lý khung trò chơi tiếp theo (không mong muốn đối với một số hệ thống)
- Hàng đợi tin nhắn đi . Trong khi một hệ thống đang xử lý một sự kiện, bất kỳ tin nhắn nào nó phát sẽ được thêm vào hàng đợi tin nhắn đi. Các thông báo không cần đợi bản cập nhật hệ thống được xử lý: chúng được xử lý "ngay lập tức" sau khi trình xử lý tin nhắn ban đầu kết thúc. Nếu việc xử lý các tin nhắn khiến các tin nhắn khác được phát, chúng cũng được thêm vào hàng đợi gửi đi, vì vậy tất cả các tin nhắn đều được xử lý cùng một khung. Vấn đề: nếu hệ thống trọn đời thực thể (tôi đã triển khai quản lý trọn đời thực thể với một hệ thống) tạo ra một thực thể, nó sẽ thông báo cho một số hệ thống A và B về nó. Trong khi hệ thống A xử lý tin nhắn, nó gây ra một chuỗi các thông điệp cuối cùng khiến thực thể được tạo ra bị phá hủy (ví dụ, một thực thể viên đạn đã được tạo ngay khi nó va chạm với một chướng ngại vật nào đó, khiến viên đạn tự hủy). Trong khi chuỗi thông báo đang được giải quyết, hệ thống B không nhận được thông báo tạo thực thể. Vì vậy, nếu hệ thống B cũng quan tâm đến thông báo hủy thực thể, thì nó sẽ nhận được nó và chỉ sau khi "chuỗi" giải quyết xong, nó mới nhận được thông báo tạo thực thể ban đầu. Điều này khiến thông báo hủy bị bỏ qua, thông báo tạo được "chấp nhận",
EDIT - TRẢ LỜI CÂU HỎI, NHẬN XÉT:
- Ai sửa đổi nội dung của tế bào trong khi hệ thống va chạm lặp lại trên chúng?
Trong khi hệ thống va chạm đang thực hiện kiểm tra va chạm trên một số thực thể và đó là hàng xóm, một vụ va chạm có thể bị phát hiện và hệ thống thực thể sẽ gửi một thông điệp sẽ được phản ứng ngay lập tức bởi các hệ thống khác. Phản ứng với tin nhắn có thể khiến các tin nhắn khác được tạo và cũng được xử lý ngay lập tức. Vì vậy, một số hệ thống khác có thể tạo ra một thông báo rằng hệ thống va chạm sau đó sẽ cần xử lý ngay lập tức (ví dụ: một thực thể di chuyển để hệ thống va chạm cần cập nhật chỉ số không gian của nó), mặc dù các kiểm tra va chạm trước đó vẫn chưa kết thúc.
- Bạn không thể làm việc với hàng đợi tin nhắn gửi đi toàn cầu?
Tôi đã thử một hàng đợi toàn cầu gần đây. Nó gây ra vấn đề mới. Vấn đề: Tôi di chuyển một thực thể xe tăng vào một thực thể tường (bể được điều khiển bằng bàn phím). Sau đó, tôi quyết định thay đổi hướng của xe tăng. Để tách bể và tường mỗi khung, CollidingRigidBodySeparationSystem di chuyển bể ra khỏi tường bằng số lượng nhỏ nhất có thể. Hướng phân tách phải ngược với một trong các hướng di chuyển của xe tăng (khi bản vẽ trò chơi bắt đầu, chiếc xe tăng sẽ trông như thể nó không bao giờ di chuyển vào tường). Nhưng hướng trở nên ngược lại với hướng MỚI, do đó di chuyển xe tăng sang một phía khác của bức tường so với ban đầu. Tại sao sự cố xảy ra: Đây là cách xử lý thư bây giờ (mã đơn giản):
public void Update(int deltaTime)
{
m_messageQueue.Enqueue(new TimePassedMessage(deltaTime));
while (m_messageQueue.Count > 0)
{
Message message = m_messageQueue.Dequeue();
this.Broadcast(message);
}
}
private void Broadcast(Message message)
{
if (m_messageListenersByMessageType.ContainsKey(message.GetType()))
{
// NOTE: all IMessageListener objects here are systems.
List<IMessageListener> messageListeners = m_messageListenersByMessageType[message.GetType()];
foreach (IMessageListener listener in messageListeners)
{
listener.ReceiveMessage(message);
}
}
}
Mã chảy như thế này (giả sử đó không phải là khung trò chơi đầu tiên):
- Hệ thống bắt đầu xử lý TimePassedMessage
- InputHandingSystem chuyển đổi các lần nhấn phím thành hành động thực thể (trong trường hợp này, một mũi tên bên trái biến thành hành động MoveWest). Hành động thực thể được lưu trữ trong thành phần ActionExecutor
- ActionExecutSystem , phản ứng với hành động thực thể, thêm MovementDirectionChangeRequestedMessage vào cuối hàng đợi tin nhắn
- MovementSystem di chuyển vị trí thực thể dựa trên dữ liệu thành phần Vận tốc và thêm thông báo PositionChangedMessage vào cuối hàng đợi. Chuyển động được thực hiện bằng cách sử dụng hướng / vận tốc của khung trước đó (giả sử là phía bắc)
- Hệ thống ngừng xử lý TimePassedMessage
- Các hệ thống bắt đầu xử lý MovementDirectionChangeRequestedMessage
- MovementSystem thay đổi vận tốc / hướng di chuyển của thực thể theo yêu cầu
- Các hệ thống ngừng xử lý MovementDirectionChangeRequestedMessage
- Hệ thống bắt đầu xử lý PositionChangedMessage
- CollisionDetectionSystem phát hiện ra rằng vì một thực thể di chuyển, nó đã chạy vào một thực thể khác (xe tăng đi vào bên trong một bức tường). Nó thêm CollisionOccuredMessage vào hàng đợi
- Hệ thống ngừng xử lý PositionChangedMessage
- Hệ thống bắt đầu xử lý CollisionOccuredMessage
- CollidingRigidBodySpayationSystem phản ứng với sự va chạm bằng cách tách bể và tường. Vì tường là tĩnh, chỉ có bể được di chuyển. Hướng di chuyển của xe tăng được sử dụng như một chỉ báo về nơi xe tăng đến từ đâu. Nó được bù theo hướng ngược lại
LGI: Khi xe tăng di chuyển khung này, nó di chuyển bằng hướng di chuyển từ khung trước đó, nhưng khi nó được tách ra, hướng di chuyển từ khung NÀY đã được sử dụng, mặc dù nó đã khác. Đó không phải là cách nó nên hoạt động!
Để ngăn chặn lỗi này, hướng di chuyển cũ cần được lưu ở đâu đó. Tôi có thể thêm nó vào một số thành phần chỉ để sửa lỗi cụ thể này, nhưng không phải trường hợp này chỉ ra một số cách xử lý thông điệp sai về cơ bản? Tại sao hệ thống phân tách nên quan tâm hướng di chuyển mà nó sử dụng? Làm thế nào tôi có thể giải quyết vấn đề này một cách thanh lịch?
- Bạn có thể muốn đọc gamadu.com/artemis để xem những gì họ đã làm với Aspects, bên nào giải quyết một số vấn đề bạn đang gặp phải.
Thật ra, tôi đã quen với Artemis khá lâu rồi. Điều tra nó là mã nguồn, đọc diễn đàn, v.v. Nhưng tôi đã thấy "Các khía cạnh" chỉ được đề cập ở một vài nơi và, theo như tôi hiểu, về cơ bản chúng có nghĩa là "Hệ thống". Nhưng tôi không thể thấy cách phía Artemis giải quyết một số vấn đề của tôi. Nó thậm chí không sử dụng tin nhắn.
- Xem thêm: "Giao tiếp thực thể: Hàng đợi tin nhắn vs Xuất bản / Đăng ký vs Tín hiệu / Slots"
Tôi đã đọc tất cả các câu hỏi gamedev.stackexchange liên quan đến các hệ thống thực thể. Điều này dường như không thảo luận về những vấn đề tôi đang gặp phải. Tui bỏ lỡ điều gì vậy?
- Xử lý hai trường hợp khác nhau, cập nhật lưới không cần dựa vào các thông báo chuyển động vì đây là một phần của hệ thống va chạm
Tôi không chắc ý của bạn là gì. Các triển khai CollisionDetectionSystem cũ hơn sẽ chỉ kiểm tra các xung đột trên một bản cập nhật (khi TimePassedMessage được xử lý), nhưng tôi phải giảm thiểu kiểm tra nhiều nhất có thể do hiệu suất. Vì vậy, tôi chuyển sang kiểm tra va chạm khi một thực thể di chuyển (hầu hết các thực thể trong trò chơi của tôi là tĩnh).