Trò chơi đối tượng Trò chơi xếp hạng - và thiết kế dựa trên thành phần


25

Tôi đã làm việc trên một số dự án sở thích trong 3-4 năm qua. Chỉ cần các trò chơi 2d và 3d đơn giản. Nhưng gần đây tôi đã bắt đầu một dự án lớn hơn. Trong vài tháng qua, tôi đã cố gắng thiết kế một lớp đối tượng trò chơi có thể là cơ sở của tất cả các đối tượng trò chơi của tôi. Vì vậy, sau nhiều lần thử và chết, tôi đã chuyển sang Google, nơi nhanh chóng chỉ cho tôi một số tệp PDF và PowerPoint GDC. Và bây giờ tôi đang cố gắng nắm bắt các đối tượng trò chơi dựa trên thành phần.

Tôi hiểu rằng công cụ tạo ra một đối tượng trò chơi và sau đó gắn các thành phần khác nhau để xử lý những thứ như sức khỏe, vật lý, kết nối mạng và bất cứ điều gì bạn làm cho chúng làm. Nhưng điều tôi không hiểu là làm thế nào thành phần X biết nếu Y đã thay đổi trạng thái của đối tượng. Giống như làm thế nào để ChemistryComponent biết người chơi còn sống, bởi vì sức khỏe được kiểm soát bởi HealthComponent ..? Và làm thế nào để HealthComponent chơi "hoạt hình người chơi đã chết"?

Tôi đã có ấn tượng rằng nó là một cái gì đó như thế này (Trong HealthComponent):

if(Health < 0) {
   AnimationComponent.PlayAnimation("played-died-animation")
}

Nhưng một lần nữa, làm thế nào để HealthComponent biết rằng đối tượng trò chơi mà nó được đính kèm để có AnimationComponent được đính kèm? Giải pháp duy nhất tôi thấy ở đây là

  1. Kiểm tra xem AnimationComponent có được gắn hay không (Hoặc bên trong mã thành phần hoặc ở phía động cơ)

  2. Có các thành phần yêu cầu các thành phần khác, nhưng điều đó dường như chống lại toàn bộ thiết kế thành phần.

  3. Viết như, HealthWithAnimationComponent, HealthNoAnimationComponent và soo on, một lần nữa dường như chống lại toàn bộ ý tưởng thiết kế thành phần.


1
Yêu câu hỏi. Tôi nên hỏi cùng một tháng trước, nhưng không bao giờ có được nó. Một vấn đề khác tôi gặp phải là khi một đối tượng trò chơi có nhiều phiên bản của cùng một thành phần (ví dụ nhiều hình ảnh động). Sẽ thật tuyệt nếu câu trả lời có thể chạm vào đó. Tôi đã kết thúc bằng cách sử dụng tin nhắn cho thông báo, với các biến được chia sẻ giữa tất cả các thành phần của đối tượng Trò chơi (vì vậy họ không cần gửi tin nhắn để nhận giá trị cho biến).
ADB

1
Tùy thuộc vào loại trò chơi, bạn có thể không có các đối tượng trò chơi có thành phần sức khỏe và không có thành phần hoạt hình. Và tất cả các gameobject này có lẽ là đại diện của một cái gì đó như Đơn vị. Vì vậy, bạn có thể loại bỏ thành phần sức khỏe và tạo UnitComponent có sức khỏe thực địa và biết về tất cả các thành phần mà đơn vị cần có. Mức độ chi tiết của các thành phần này không thực sự giúp ích gì cả - thực tế hơn khi có một thành phần cho mỗi miền (kết xuất, âm thanh, vật lý, logic trò chơi).
Kikaimaru

Câu trả lời:


11

Trong tất cả các ví dụ của bạn, có một vấn đề khủng khiếp. Thành phần sức khỏe cần biết về mọi loại thành phần có thể cần phải đáp ứng với thực thể chết. Do đó, không có kịch bản nào của bạn là phù hợp. Thực thể của bạn có một thành phần sức khỏe. Nó có một thành phần hoạt hình. Không phụ thuộc hoặc biết về người khác. Họ liên lạc qua một hệ thống nhắn tin.

Khi thành phần sức khỏe phát hiện thực thể đó đã 'chết', nó sẽ gửi một thông điệp 'Tôi đã chết'. Trách nhiệm của thành phần hoạt hình là trả lời tin nhắn này bằng cách phát hoạt hình thích hợp.

Thành phần sức khỏe không gửi tin nhắn trực tiếp đến thành phần hoạt hình. Có thể nó phát nó đến mọi thành phần trong thực thể đó, có thể cho toàn bộ hệ thống; có lẽ thành phần hoạt hình cần cho hệ thống nhắn tin biết rằng nó quan tâm đến tin nhắn 'Tôi đã chết'. Có nhiều cách để thực hiện hệ thống nhắn tin. Tuy nhiên, bạn triển khai nó, vấn đề là thành phần sức khỏe và thành phần hoạt hình không bao giờ cần biết hoặc quan tâm nếu có thành phần khác và việc thêm các thành phần mới sẽ không bao giờ yêu cầu sửa đổi các thành phần hiện có để gửi cho chúng thông điệp phù hợp.


Okey, điều này có ý nghĩa. Nhưng ai tuyên bố các "trạng thái" như "chết", hay "cổng thông tin bị phá vỡ", v.v ... Thành phần hay động cơ? Bởi vì việc thêm một trạng thái 'chết' vào một thứ sẽ không bao giờ có thành phần sức khỏe kèm theo dường như là một sự lãng phí đối với tôi. Tôi đoán tôi sẽ chỉ đi sâu vào và bắt đầu thử nghiệm một số mã và xem những gì hoạt động.
hayer

Michael và Patrick Hughes có câu trả lời đúng ở trên. Các thành phần chỉ là dữ liệu; Vì vậy, nó không thực sự là thành phần sức khỏe phát hiện khi thực thể đã chết và gửi tin nhắn, đó là một phần logic cao hơn dành riêng cho trò chơi. Làm thế nào để trừu tượng đó là tùy thuộc vào bạn. Tình trạng thực sự của cái chết không bao giờ cần phải được lưu trữ ở bất cứ đâu. Đối tượng đã chết nếu sức khỏe của nó là <0 và thành phần sức khỏe có thể gói gọn bit logic kiểm tra dữ liệu đó mà không phá vỡ 'không hành vi!' hạn chế nếu bạn chỉ coi những thứ sửa đổi trạng thái của thành phần là hành vi.
Blecki

Chỉ tò mò, làm thế nào bạn sẽ xử lý một MovementComponent? Khi phát hiện đầu vào, nó cần tăng vận tốc trong PositionComponent. Thông điệp sẽ như thế nào?
Mẹo48

8

Cách mà Artemis giải quyết vấn đề là không xử lý trong Thành phần. Các thành phần chỉ chứa dữ liệu họ cần. Hệ thống đọc nhiều loại thành phần và làm bất cứ điều gì xử lý là cần thiết.

Vì vậy, trong trường hợp của bạn, bạn có thể có một Hệ thống kết xuất đọc trong HealthComponent (và những người khác) và phát các hàng đợi lên các hình động phù hợp. Tách dữ liệu khỏi các chức năng theo cách này giúp dễ dàng giữ cho các phụ thuộc được quản lý đúng cách.


Đây cuối cùng là một cách hay để xử lý vấn đề: Các thành phần đại diện cho các thuộc tính trong khi các Hệ thống liên kết các thuộc tính khác nhau và sử dụng các thuộc tính đó để thực hiện công việc. Đó là một sự thay đổi lớn so với suy nghĩ OOP truyền thống và làm cho đầu của một số người bị tổn thương =)
Patrick Hughes

Okey, bây giờ tôi thực sự đã mất .. "Ngược lại, trong ES, nếu bạn có 100 đơn vị trên chiến trường, mỗi đơn vị được đại diện bởi một Thực thể, thì bạn không có bản sao của mỗi phương thức có thể được gọi trên một đơn vị - bởi vì Các thực thể không chứa các phương thức. Các thành phần cũng không chứa các phương thức. Thay vào đó, bạn có một hệ thống bên ngoài cho từng khía cạnh và hệ thống bên ngoài đó chứa tất cả các phương thức có thể được gọi trên bất kỳ Thực thể nào có Thành phần đánh dấu nó tương thích với nó hệ thống. " Chà, dữ liệu trong GunComponent được lưu trữ ở đâu? Giống như vòng, vv Nếu tất cả các thực thể chia sẻ cùng một thành phần.
hayer

1
Theo tôi hiểu, tất cả các thực thể không chia sẻ cùng một thành phần, mỗi thực thể có thể có N thể hiện thành phần được đính kèm với chúng. Sau đó, một Hệ thống truy vấn trò chơi để biết danh sách tất cả các thực thể có các thể hiện thành phần mà chúng quan tâm gắn liền với chúng và sau đó thực hiện bất kỳ xử lý nào trên chúng
Jake Woods

Điều này chỉ di chuyển vấn đề xung quanh. Làm thế nào để một hệ thống biết những thành phần nào sẽ sử dụng? Một hệ thống cũng có thể cần các hệ thống khác (hệ thống StateMachine có thể muốn gọi cho một hình ảnh động chẳng hạn). Tuy nhiên, nó không giải quyết được vấn đề của WHO sở hữu dữ liệu. Trong thực tế, việc thực hiện đơn giản hơn là có một từ điển trong đối tượng trò chơi và mỗi hệ thống tạo ra các biến của anh ta trong đó.
ADB

Nó không di chuyển vấn đề xung quanh nhưng đến một nơi có nhiều khả năng hơn. Hệ thống có các thành phần có liên quan cứng có dây. Các hệ thống có thể giao tiếp với nhau thông qua các Thành phần (StateMachine có thể đặt giá trị thành phần mà Animation đọc để biết phải làm gì (hoặc có thể kích hoạt Sự kiện). Cách tiếp cận từ điển nghe có vẻ như Mẫu Thuộc tính cũng có thể hoạt động. Các thành phần là các thuộc tính liên quan được nhóm lại với nhau và chúng có thể được kiểm tra tĩnh. Không có lỗi kỳ lạ vì bạn đã thêm "Dammage" ở một nơi nhưng đã cố truy xuất nó bằng cách sử dụng "Thiệt hại" ở nơi khác.
Michael

6

Trong mã của bạn, bạn có thể có các cách (tôi đã sử dụng chúng, có thể một số cách khác tồn tại) để biết nếu đối tượng thay đổi trạng thái:

  1. Gửi tin nhắn.
  2. Đọc dữ liệu trực tiếp từ thành phần.

1) Kiểm tra xem AnimationComponent có được gắn hay không (Hoặc bên trong mã thành phần hoặc ở phía động cơ)

Đối với điều này tôi đã sử dụng, 1. Hàm HasComponent của GameObject hoặc 2. khi bạn đính kèm thành phần, bạn có thể kiểm tra các phụ thuộc trong một số hàm xây dựng hoặc 3. Nếu tôi biết chắc chắn đối tượng đó có thành phần này, tôi chỉ sử dụng nó.

2) Có các thành phần yêu cầu các thành phần khác, nhưng điều đó dường như chống lại toàn bộ thiết kế thành phần.

Trong một số bài viết tôi đã đọc, trong các thành phần hệ thống lý tưởng không phụ thuộc vào nhau, nhưng trong thực tế, nó không phải như vậy.

3) Viết như, HealthWithAnimationComponent, HealthNoAnimationComponent và soo on, một lần nữa dường như chống lại toàn bộ ý tưởng thiết kế thành phần.

Đó là một ide xấu để viết các thành phần như vậy. Trong ứng dụng của tôi, tôi đã tạo thành phần Sức khỏe độc ​​lập nhất. Bây giờ tôi đang suy nghĩ về một số mẫu Observer thông báo cho người đăng ký về một số sự kiện cụ thể (ví dụ: "hit", "heal", v.v.). Vì vậy, AnimationComponent phải tự quyết định khi chơi hoạt hình.

Nhưng khi tôi đọc bài viết về CBES, nó đã gây ấn tượng với tôi, vì vậy bây giờ tôi rất hạnh phúc khi tôi sử dụng CBES và khám phá những khả năng mới của nó.


1
Chà, google.no/ từ @ slide 16
hayer

@bobenko, vui lòng cung cấp một liên kết đến bài viết về CBES. Tôi cũng rất thú vị trong đó;)
Edward83

1
lambdor.net/?p=171 @ bottom, đây là một bản tóm tắt câu hỏi của tôi Làm thế nào chức năng khác nhau có thể được định nghĩa theo các thành phần tương đối phức tạp, không cơ bản? Các thành phần cơ bản nhất là gì? Các thành phần cơ bản khác với các hàm thuần túy theo cách nào? Làm thế nào các thành phần hiện có có thể tự động giao tiếp với các tin nhắn mới từ các thành phần mới? Điểm bỏ qua một thông điệp mà một thành phần không biết là gì? Rốt cuộc điều gì đã xảy ra với mô hình đầu vào-quá trình-đầu ra?
hayer

1
đây là câu trả lời hay trên CBES stackoverflow.com/a/3495647/903195 hầu hết các bài báo tôi đã nghiên cứu đều từ câu trả lời này. Tôi bắt đầu và truyền cảm hứng với cowboyprogramming.com/2007/01/05/evolve-your-heirachy sau đó trong Gems 5 (như tôi nhớ) có bài viết hay với các ví dụ.
Yevhen

Nhưng những gì về một khái niệm lập trình phản ứng chức năng khác, đối với tôi câu hỏi này vẫn còn mở, nhưng có thể đối với bạn là một hướng tốt cho các nghiên cứu.
Yevhen

3

Nó giống như Michael, Patrick Hughes và Blecki nói. Giải pháp để tránh đơn giản là di chuyển vấn đề xung quanh là từ bỏ ý thức hệ gây ra vấn đề ngay từ đầu.

Nó ít hơn và giống như lập trình chức năng. Khi tôi bắt đầu thử nghiệm với Thiết kế dựa trên thành phần, tôi đã phát hiện ra vấn đề này. Tôi đã googled thêm một số, và tôi thấy "Lập trình phản ứng Functive" là giải pháp.

Bây giờ các thành phần của tôi không là gì ngoài một tập hợp các biến và trường mô tả trạng thái hiện tại của nó. Sau đó, tôi có một loạt các lớp "Hệ thống" cập nhật tất cả các thành phần có liên quan đến chúng. Phần phản ứng đạt được bằng cách chạy Hệ thống theo thứ tự được xác định rõ. Điều này đảm bảo rằng bất kỳ Hệ thống nào tiếp theo đều được xử lý và cập nhật, và bất kỳ thành phần và thực thể nào nó dự định đọc và cập nhật, hệ thống luôn hoạt động trên dữ liệu cập nhật.

Tuy nhiên, theo một cách nào đó, bạn vẫn có thể tuyên bố rằng vấn đề đã di chuyển một lần nữa, mặc dù. Bởi vì điều gì sẽ xảy ra nếu hệ thống của bạn không cần phải chạy theo thứ tự nào? Điều gì xảy ra nếu có các mối quan hệ theo chu kỳ và chỉ là vấn đề thời gian trước khi bạn nhìn chằm chằm vào một mớ hỗn độn của if-if và switch statement? Đây là một hình thức nhắn tin ngầm, không? Thoạt nhìn, tôi nghĩ đó là một rủi ro nhỏ. Thông thường, mọi thứ được xử lý theo thứ tự. Một cái gì đó như: Đầu vào của người chơi -> Vị trí thực thể -> Phát hiện va chạm -> Logic trò chơi -> Kết xuất -> Bắt đầu lại. Trong trường hợp đó, bạn sẽ có một Hệ thống cho mỗi Hệ thống, cung cấp cho mỗi Hệ thống một phương thức update () và sau đó chạy chúng theo trình tự trong gameloop của bạn.

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.