Chiến thuật di chuyển logic kết xuất ra khỏi lớp GameObject


10

Khi tạo trò chơi, bạn thường tạo đối tượng trò chơi sau mà tất cả các thực thể kế thừa:

public class GameObject{
    abstract void Update(...);
    abstract void Draw(...);
}

Vì vậy, trong vòng cập nhật, bạn lặp lại tất cả các đối tượng trò chơi và cho chúng cơ hội thay đổi trạng thái, sau đó trong vòng rút thăm tiếp theo, bạn lặp lại lần nữa trên tất cả các đối tượng trò chơi và cho chúng cơ hội tự vẽ.

Mặc dù điều này hoạt động khá tốt trong một trò chơi đơn giản với trình kết xuất chuyển tiếp đơn giản, nhưng nó thường dẫn đến một vài đối tượng trò chơi khổng lồ cần lưu trữ mô hình của chúng, nhiều kết cấu và tệ nhất là phương pháp vẽ béo tạo ra sự khớp nối chặt chẽ giữa đối tượng trò chơi, chiến lược kết xuất hiện tại và bất kỳ lớp liên quan đến kết xuất nào.

Nếu tôi thay đổi chiến lược kết xuất từ ​​chuyển tiếp sang hoãn lại, tôi sẽ phải cập nhật rất nhiều đối tượng trò chơi. Và các đối tượng trò chơi tôi tạo ra không thể tái sử dụng như chúng có thể. Tất nhiên sự kế thừa và / hoặc thành phần có thể giúp tôi chống lại sự sao chép mã và làm cho việc thay đổi triển khai dễ dàng hơn một chút nhưng nó vẫn cảm thấy thiếu.

Một cách tốt hơn, có lẽ, sẽ là loại bỏ hoàn toàn phương thức Draw khỏi lớp GameObject và tạo một lớp Renderer. GameObject vẫn cần chứa một số dữ liệu về hình ảnh của nó, như mô hình nào để thể hiện nó và họa tiết nào sẽ được vẽ trên mô hình, nhưng cách thực hiện sẽ được để lại cho trình kết xuất. Tuy nhiên, thường có rất nhiều trường hợp viền trong kết xuất, vì vậy mặc dù điều này sẽ loại bỏ khớp nối chặt chẽ từ GameObject sang Trình kết xuất, Trình kết xuất vẫn phải biết về tất cả các đối tượng trò chơi sẽ làm cho nó béo, tất cả đều biết và liên kết chặt chẽ. Điều này sẽ vi phạm khá nhiều thực hành tốt. Có lẽ Data-định hướng-thiết kế có thể thực hiện các mẹo. Các đối tượng trò chơi chắc chắn sẽ là dữ liệu, nhưng trình kết xuất sẽ được điều khiển bởi điều này như thế nào? Tôi không chắc.

Vì vậy, tôi thua lỗ và không thể nghĩ ra một giải pháp tốt. Tôi đã thử sử dụng các nguyên tắc của MVC và trong quá khứ tôi đã có một số ý tưởng về cách sử dụng nó trong các trò chơi, nhưng gần đây nó dường như không thể áp dụng như tôi nghĩ. Tôi rất muốn biết làm thế nào tất cả các bạn giải quyết vấn đề này.

Dù sao, hãy tóm tắt lại, tôi quan tâm đến cách đạt được các mục tiêu thiết kế sau đây.

  • Không có logic kết xuất trong đối tượng trò chơi
  • Khớp nối lỏng lẻo giữa các đối tượng trò chơi và công cụ kết xuất
  • Không ai biết trình kết xuất
  • Tốt nhất là chuyển đổi thời gian chạy giữa các công cụ kết xuất

Thiết lập dự án lý tưởng sẽ là một 'logic trò chơi' riêng biệt và kết xuất dự án logic không cần tham chiếu lẫn nhau.

Suy nghĩ này đã bắt đầu khi tôi nghe John Carmack nói trên twitter rằng anh ta có một hệ thống linh hoạt đến mức anh ta có thể trao đổi các công cụ kết xuất trong thời gian chạy và thậm chí có thể bảo hệ thống của mình sử dụng cả hai trình kết xuất (trình kết xuất phần mềm và trình kết xuất được tăng tốc phần cứng) đồng thời để anh ta có thể kiểm tra sự khác biệt. Các hệ thống tôi đã lập trình cho đến nay thậm chí không gần linh hoạt

Câu trả lời:


7

Bước đầu tiên nhanh chóng để hủy ghép nối:

Các đối tượng trò chơi tham chiếu một định danh về hình ảnh của chúng nhưng không phải là dữ liệu, hãy nói điều gì đó đơn giản như một chuỗi. Ví dụ: "human_male"

Trình kết xuất có trách nhiệm tải và duy trì các tham chiếu "human_male" và gửi lại cho các đối tượng một tay cầm để sử dụng.

Ví dụ trong mã giả khủng khiếp:

GameObject( initialization parameters )
  me.render_handle = Renderer_Create( parameters.render_string )

- elsewhere
Renderer_Create( string )

  new data handle = Resources_Load( string );
  return new data handle

- some time later
GameObject( something happens to me parameters )
  me.state = something.what_happens
  Renderer_ApplyState( me.render_handle, me.state.effect_type )

- some time later
Renderer_Render()
  for each renderable thing
    for each rendering back end
        setup graphics for thing.effect
        render it

- finally
GameObject_Destroy()
  Renderer_Destroy( me.render_handle )

Xin lỗi vì sự lộn xộn đó, dù sao thì điều kiện của bạn cũng được đáp ứng bởi sự thay đổi đơn giản đó khỏi OOP thuần túy dựa trên việc nhìn vào những thứ như các đối tượng trong thế giới thực và vào OOP dựa trên trách nhiệm.

  • Không có logic kết xuất trong đối tượng trò chơi (thực hiện, tất cả các đối tượng biết là một tay cầm để nó có thể áp dụng hiệu ứng cho chính nó)
  • Khớp nối lỏng lẻo giữa các đối tượng trò chơi và công cụ kết xuất (thực hiện, tất cả các liên hệ đều thông qua một tay cầm trừu tượng, các trạng thái có thể được áp dụng và không phải làm gì với các trạng thái đó)
  • Không có tất cả các trình kết xuất biết (thực hiện, chỉ biết về chính nó)
  • Tốt nhất là chuyển đổi thời gian chạy giữa các công cụ kết xuất (điều này được thực hiện ở giai đoạn Renderer_Render (), bạn phải viết cả hai mặt sau)

Các từ khóa bạn có thể tìm kiếm để vượt ra ngoài việc tái cấu trúc các lớp đơn giản sẽ là "hệ thống thực thể / thành phần" và "tiêm phụ thuộc" và các mẫu GUI "MVC" có khả năng chỉ để làm cho bánh răng cũ quay tròn.


Điều này cực kỳ khác biệt so với bất cứ điều gì tôi đã làm trước đây, có vẻ như nó có khá nhiều tiềm năng. May mắn thay, tôi không bị hạn chế bởi bất kỳ động cơ hiện có nào vì vậy tôi chỉ có thể sửa đổi. Tôi cũng sẽ tìm kiếm các thuật ngữ bạn đã đề cập, mặc dù tiêm phụ thuộc luôn khiến não tôi bị tổn thương: P.
Roy T.

2

Những gì tôi đã làm cho động cơ của riêng tôi là nhóm mọi thứ thành các mô-đun. Vì vậy, tôi có GameObjectlớp học của mình và nó nắm giữ để:

  • ModuleSprite - vẽ các họa tiết
  • ModuleWeapon - bắn súng
  • ModuleScriptingBase - kịch bản
  • ModuleParticles - hiệu ứng hạt
  • ModuleCollision - phát hiện va chạm

Vì vậy, tôi có một Playerlớp học và một Bulletlớp học. Cả hai đều xuất phát từ GameObjectvà được thêm vào Scene. Nhưng Playercó các mô-đun sau:

  • ModuleSprite
  • Mô-đun
  • ModuleParticles
  • Mô-đun

Bulletcó các mô-đun:

  • ModuleSprite
  • Mô-đun

Cách tổ chức này giúp tránh "Kim cương tử thần" nơi bạn có a Vehicle, a VehicleLandvà a VehicleWatervà bây giờ bạn muốn a VehicleAmphibious. Thay vào đó bạn có một Vehiclevà nó có thể có một ModuleWatervà a ModuleLand.

Đã thêm phần thưởng: bạn có thể tạo các đối tượng bằng cách sử dụng một bộ thuộc tính. Tất cả những gì bạn phải biết là kiểu cơ sở (Người chơi, Kẻ thù, Đạn, v.v.) và sau đó tạo tay cầm cho các mô-đun bạn cần cho loại đó.

Trong Cảnh của tôi, tôi làm như sau:

  • Gọi Updatecho tất cả các GameObjecttay cầm.
  • Kiểm tra va chạm và phản ứng va chạm cho những người có ModuleCollisiontay cầm.
  • Gọi UpdatePostcho tất cả các GameObjecttay cầm để biết về vị trí cuối cùng của họ sau khi vật lý.
  • Tiêu diệt các đối tượng có cờ của họ được thiết lập.
  • Thêm các đối tượng mới từ m_ObjectsCreateddanh sách vào m_Objectsdanh sách.

Và tôi có thể tổ chức nó hơn nữa: bằng các mô-đun thay vì theo đối tượng. Sau đó, tôi sẽ đưa ra một danh sách ModuleSprite, cập nhật một loạt các ModuleScriptingBaseva chạm với một danh sách ModuleCollision.


Âm thanh như thành phần đến mức tối đa! Rất đẹp. Tôi không thấy nhiều lời khuyên cụ thể kết xuất ở đây mặc dù. Làm thế nào để bạn xử lý điều đó, chỉ bằng cách thêm các mô-đun khác nhau?
Roy T

Ồ vâng. Đó là nhược điểm của hệ thống này: nếu bạn có một yêu cầu cụ thể đối với GameObject(ví dụ: cách kết xuất "con rắn" của Sprites), bạn sẽ cần phải tạo một đứa trẻ ModuleSpritecho chức năng cụ thể đó ( ModuleSpriteSnake) hoặc thêm một mô-đun mới ( ModuleSnake). May mắn thay, họ chỉ là con trỏ, nhưng tôi đã thấy mã nơi GameObjectthực sự mọi thứ mà một đối tượng có thể làm.
hiệp sĩ666
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.