2010-02-09 41 views
21

Tôi đang thiết kế một trò chơi đơn giản, sử dụng vật lý Java 2D và Newton. Hiện nay chính "trò chơi vòng lặp" của tôi trông giống như sau:Thiết kế trò chơi theo cách OO

do { 
    for (GameEntity entity : entities) { 
    entity.update(gameContext); 
    } 

    for (Drawable drawable : drawables) { 
    drawable.draw(graphics2d); 
    } 
} while (gameRunning); 

Khi một thực thể được hướng dẫn để cập nhật bản thân nó sẽ tự điều chỉnh tốc độ và vị trí của nó dựa trên các lực lượng hiện tại áp dụng cho nó. Tuy nhiên, tôi cần các thực thể để thể hiện hành vi khác; ví dụ. nếu một "kẻ xấu" bị bắn bởi một người chơi thì thực thể đó sẽ bị phá hủy và bị loại bỏ khỏi thế giới game.

Câu hỏi của tôi: Cách tốt nhất để đạt được điều này theo cách hướng đối tượng là gì? Tất cả các ví dụ tôi đã thấy cho đến nay kết hợp vòng lặp trò chơi vào lớp Thiên Chúa được gọi là Game, thực hiện các bước sau: phát hiện va chạm, kiểm tra nếu bị giết chết, kiểm tra nếu người chơi bị giết, sơn lại , vv và đóng gói tất cả trạng thái trò chơi (cuộc sống còn lại, v.v.). Nói cách khác, nó rất thủ tụctất cả logic nằm trong lớp Trò chơi. Bất cứ ai có thể giới thiệu một cách tiếp cận tốt hơn?

Dưới đây là các tùy chọn Tôi đã nghĩ đến việc cho đến nay:

  • Vượt qua một GameContext cho từng đối tượng mà từ đó các thực thể có thể loại bỏ chính nó nếu cần thiết hoặc cập nhật trạng thái của game (ví dụ để "không chạy" nếu người chơi bị giết).
  • Đăng ký mỗi GameEntity làm người nghe đến lớp học trung tâm Game và thực hiện phương pháp tiếp cận theo hướng sự kiện; ví dụ. va chạm sẽ dẫn đến việc CollisionEvent bị bắn cho hai người tham gia trong vụ va chạm.
+2

Tôi nghĩ rằng tôi đã tìm thấy lỗi của bạn - đó là 'while (gameRunning) 'bạn muốn có –

Trả lời

14

Tôi đã làm việc chặt chẽ với hai game engine thương mại và họ làm theo một mô hình tương tự:

  • Đối tượng đại diện cho các thành phần hoặc các khía cạnh của một tổ chức trò chơi (như vật lý, renderable, bất cứ điều gì), chứ không phải là toàn bộ thực thể . Đối với mỗi loại thành phần có một danh sách khổng lồ các thành phần, một cho mỗi thể hiện thực thể có thành phần.

  • Loại đối tượng 'trò chơi' chính nó chỉ là một ID duy nhất. Mỗi danh sách khổng lồ các thành phần có một bản đồ để tìm kiếm thành phần (nếu có) tương ứng với một ID thực thể.

  • Nếu thành phần yêu cầu bản cập nhật, dịch vụ này được gọi bởi dịch vụ hoặc đối tượng hệ thống. Mỗi dịch vụ được cập nhật trực tiếp từ vòng lặp trò chơi. Ngoài ra, bạn có thể gọi các dịch vụ từ một đối tượng lập lịch để xác định thứ tự cập nhật từ biểu đồ phụ thuộc.

Dưới đây là những lợi thế của phương pháp này:

  • Bạn có thể tự do kết hợp chức năng mà không cần viết các lớp mới cho mỗi kết hợp hoặc sử dụng thừa kế phức tạp cây.

  • Hầu như không có chức năng mà bạn có thể giả định về tất cả các trò chơi thực thể mà bạn có thể đặt trong một trò chơi lớp cơ sở thực thể (những gì hiện một ánh sáng có điểm chung với một chiếc xe đua hay một bầu trời-box ?)

  • ID-to-thành phần look-up có vẻ đắt tiền, nhưng các dịch vụ đang làm hầu hết các công việc chuyên sâu bởi lặp qua tất cả các thành phần của một loại cụ thể. Trong những trường hợp này, nó hoạt động tốt hơn để lưu trữ tất cả dữ liệu bạn cần trong một danh sách gọn gàng duy nhất.

+0

@Evan: Cảm ơn câu trả lời của bạn. Tôi có nghĩa là để hỏi bạn: Làm thế nào bạn sẽ xử lý việc loại bỏ một thực thể trong trường hợp này? Ví dụ, giả sử bạn có một khía cạnh Collidable, và CollisionManager của bạn phát hiện một xung đột, điều này sẽ khiến cho thực thể bị loại bỏ. Có lẽ CollisionManager chỉ tham chiếu đến khía cạnh Collidable của thực thể, và vì vậy cách tiếp cận nào bạn thực hiện để loại bỏ ** tất cả các khía cạnh của thực thể (Drawable, Collidable, vv) khỏi các danh sách khác nhau? – Adamski

+1

Phần còn thiếu mà tôi không đề cập đến là tin nhắn hoặc sự kiện. Mỗi hệ thống có thể đăng ký bất kỳ loại tin nhắn nào hoặc đăng tin nhắn. Hệ thống vật lý có thể đăng thông điệp "Va chạm" mà hệ thống chơi trò chơi có thể đăng ký và có thể đăng thông báo "xóa đối tượng" để phản hồi. Một hệ thống khác chịu trách nhiệm tạo và xóa các thực thể có thể đăng ký với loại thông báo xóa thực thể. Đây là công việc nhiều hơn so với các cuộc gọi hàm trực tiếp, nhưng nó là tất cả trong tên của tách. –

6

Trong một công cụ cụ thể mà tôi đã làm việc, chúng tôi đã tách logic khỏi đại diện đồ họa và sau đó có các đối tượng sẽ gửi tin nhắn cho những gì họ muốn làm. Chúng tôi đã làm điều này để chúng tôi có thể có các trò chơi tồn tại trên một máy tính cục bộ hoặc được nối mạng và chúng không thể phân biệt được với nhau từ một quan điểm mã. (Mẫu lệnh)

Chúng tôi cũng có mô hình vật lý thực tế được thực hiện trong một đối tượng riêng có thể thay đổi khi đang di chuyển. Điều này cho phép chúng tôi dễ dàng gây rối với trọng lực, v.v.

Chúng tôi đã sử dụng nhiều mã định hướng sự kiện (mẫu người nghe) và rất nhiều bộ tính giờ.

Ví dụ: chúng tôi đã có một lớp cơ sở cho một đối tượng có thể giao nhau có thể nghe sự kiện xung đột. Chúng tôi đã phân loại nó thành một hộp sức khỏe. Khi va chạm, nếu nó bị tấn công bởi một thực thể người chơi, nó đã gửi một Lệnh tới người va chạm rằng nó sẽ nhận được sức khỏe, gửi một tin nhắn để phát ra âm thanh cho tất cả những gì có thể nghe thấy, va chạm bất hoạt, kích hoạt hoạt ảnh để xóa đồ họa khỏi biểu đồ cảnh và đặt hẹn giờ để tự khôi phục sau này. Nghe có vẻ phức tạp, nhưng thực sự thì không.

Nếu tôi nhớ lại (và nó đã được 12 năm), chúng tôi đã có khái niệm trừu tượng của cảnh, do đó, một trò chơi là một chuỗi các cảnh. Khi một cảnh được hoàn thành, một sự kiện đã bị sa thải, thường sẽ gửi một lệnh lấy cảnh hiện tại và bắt đầu một cảnh khác.

+0

Cảm ơn - Một số lời khuyên tốt ở đây. – Adamski

+0

Có lẽ nên sử dụng mẫu hòa giải ở đây để tạo điều kiện cho sự kiện/đăng ký/xử lý và dữ liệu sự kiện, nếu không bạn phải ghép nối tất cả các hệ thống con và các thực thể trò chơi với nhau. – dvide

3

Tôi không đồng ý rằng vì bạn có một Lớp trò chơi chính, tất cả logic phải xảy ra trong lớp học đó.

Over-đơn giản hóa ở đây bắt chước ví dụ của bạn chỉ để làm cho quan điểm của tôi:

mainloop: 
    moveEntities() 
    resolveCollisions() [objects may "disappear"/explode here] 
    drawEntities()  [drawing before or after cleanEntitites() ain't an issue, a dead entity won't draw itself] 
    cleanDeadEntities() 

Bây giờ bạn có một lớp bong bóng:

Bubble implements Drawable { 

handle(Needle needle) { 
    if (needle collide with us) { 
     exploded = true; 
    } 
} 

draw (...) { 
    if (!exploded) { 
     draw(); 
    } 
    } 
} 

Vì vậy, chắc chắn, có một vòng lặp chính mà sẽ chăm sóc của truyền các thông điệp xung quanh giữa các thực thể nhưng logic liên quan đến va chạm giữa Bubble và Needle chắc chắn không nằm trong lớp Game chính.

Tôi khá chắc chắn rằng ngay cả trong trường hợp của bạn tất cả các logic liên quan đến phong trào này không xảy ra trong lớp học chính.

Vì vậy, tôi không đồng ý với tuyên bố của bạn, rằng bạn viết bằng chữ đậm, rằng "tất cả logic xảy ra trong lớp chính".

Điều này đơn giản là không chính xác.

Đối với thiết kế tốt: nếu bạn có thể dễ dàng cung cấp "chế độ xem" khác cho trò chơi của mình (ví dụ như bản đồ thu nhỏ) và nếu bạn có thể dễ dàng viết mã "trình phát lại hoàn hảo cho khung hình" thiết kế có lẽ không phải là xấu (có nghĩa là: bằng cách ghi lại các yếu tố đầu vào và thời gian mà chúng đã xảy ra, bạn sẽ có thể tạo lại trò chơi chính xác như nó được chơi. Đó là cách Age of Empires, Warcraft 3, v.v. phát lại: nó chỉ là đầu vào của người dùng và thời gian mà chúng xảy ra được ghi lại [đó cũng là lý do tại sao các tệp phát lại thường quá nhỏ]).

+0

Cảm ơn. Tôi đã không chủ trương loại bỏ vòng lặp trò chơi hoàn toàn, chỉ cần giữ nó càng đơn giản càng tốt vì vậy những gì bạn đã nói có ý nghĩa. – Adamski

1

game là thử nghiệm trong việc giữ mô hình và xem riêng biệt. Nó sử dụng mẫu người quan sát để thông báo cho (các) lượt thay đổi trong trạng thái của trò chơi, nhưng các sự kiện có lẽ sẽ cung cấp một bối cảnh phong phú hơn. Ban đầu, mô hình được thúc đẩy bởi đầu vào bàn phím, nhưng sự tách biệt giúp bạn dễ dàng thêm hoạt ảnh định thời gian.

Phụ lục: Bạn cần giữ mô hình của trò chơi riêng biệt, nhưng bạn có thể tái tạo mô hình đó thành nhiều lớp theo yêu cầu.

2

Tôi viết động cơ của riêng mình (thô & bẩn), nhưng động cơ được xây dựng sẵn có mô hình OO phong nha là Ogre. Tôi khuyên bạn nên xem xét nó (đó là mô hình đối tượng/API). Việc gán nút là một chút sôi nổi, nhưng nó làm cho cảm giác hoàn hảo càng có nhiều bạn nhìn vào nó. Nó cũng được ghi nhận rất tốt với rất nhiều ví dụ về trò chơi hoạt động.

Tôi đã học được một vài thủ thuật từ chính bản thân nó.

+0

Nội dung tuyệt vời - Sẽ kiểm tra. – Adamski

Các vấn đề liên quan