Tư vấn về các mẫu thiết kế / kiến ​​trúc trò chơi


16

Bây giờ tôi đã làm việc với một game nhập vai 2d và tôi nhận ra rằng tôi đã đưa ra một số quyết định thiết kế tồi. Có một vài điều đặc biệt gây ra vấn đề cho tôi, vì vậy tôi đã tự hỏi những loại thiết kế mà người khác sử dụng để khắc phục chúng, hoặc sẽ sử dụng.

Để có một nền tảng nhỏ, tôi bắt đầu làm việc với nó trong thời gian rảnh vào mùa hè năm ngoái. Ban đầu tôi làm game ở C #, nhưng khoảng 3 tháng trước, đã quyết định chuyển sang C ++. Tôi muốn có một xử lý tốt về C ++ vì đã được một lúc kể từ khi tôi sử dụng nó rất nhiều, và nghĩ rằng một dự án thú vị như thế này sẽ là một động lực tốt. Tôi đã sử dụng rộng rãi thư viện boost và đã sử dụng SFML cho đồ họa và FMOD cho âm thanh.

Tôi có một chút mã được viết, nhưng đang xem xét loại bỏ nó và bắt đầu lại.

Đây là những lĩnh vực quan tâm chính mà tôi có và muốn có một số ý kiến ​​về cách thức phù hợp mà người khác đã giải quyết chúng hoặc sẽ giải quyết chúng.

1. Phụ thuộc theo chu kỳ Khi tôi đang chơi trò chơi trong C #, tôi không thực sự phải lo lắng về điều này vì nó không phải là vấn đề ở đó. Chuyển sang C ++, điều này đã trở thành một vấn đề khá lớn và khiến tôi nghĩ rằng tôi có thể đã thiết kế mọi thứ không chính xác. Tôi thực sự không thể tưởng tượng làm thế nào để tách lớp của tôi và vẫn để họ làm những gì tôi muốn. Dưới đây là một vài ví dụ về chuỗi phụ thuộc:

Tôi có một lớp hiệu ứng trạng thái. Lớp này có một số phương thức (Áp dụng / Không thích hợp, Đánh dấu, v.v.) để áp dụng các hiệu ứng của nó đối với một ký tự. Ví dụ,

virtual void TickCharacter(Character::BaseCharacter* character, Battles::BattleField *field, int ticks = 1);

Các chức năng này sẽ được gọi mỗi khi nhân vật gây ra hiệu ứng trạng thái thay phiên nhau. Nó sẽ được sử dụng để thực hiện các hiệu ứng như Regen, Poison, v.v. Tuy nhiên, nó cũng giới thiệu các phụ thuộc vào lớp BaseCharacter và lớp BattleField. Đương nhiên, lớp BaseCharacter cần theo dõi những hiệu ứng trạng thái nào hiện đang hoạt động trên chúng để đó là một sự phụ thuộc theo chu kỳ. Battlefield cần theo dõi các bên chiến đấu và lớp nhóm có một danh sách BaseChar character giới thiệu một sự phụ thuộc theo chu kỳ khác.

2 - Sự kiện

Trong C #, tôi đã sử dụng rộng rãi các đại biểu để tham gia vào các sự kiện trên các nhân vật, chiến trường, v.v. (ví dụ, có một đại biểu khi thay đổi sức khỏe của nhân vật, khi thay đổi chỉ số, khi thêm / xóa hiệu ứng trạng thái, v.v. .) và các thành phần đồ họa / chiến trường sẽ kết nối với các đại biểu đó để thực thi các hiệu ứng của họ. Trong C ++, tôi đã làm một cái gì đó tương tự. Rõ ràng là không có tương đương trực tiếp với các đại biểu C #, vì vậy thay vào đó tôi đã tạo ra một cái gì đó như thế này:

typedef boost::function<void(BaseCharacter*, int oldvalue, int newvalue)> StatChangeFunction;

và trong lớp nhân vật của tôi

std::map<std::string, StatChangeFunction> StatChangeEventHandlers;

Bất cứ khi nào chỉ số của nhân vật thay đổi, tôi sẽ lặp lại và gọi mọi StatChangeFunction trên bản đồ. Trong khi nó hoạt động, tôi lo lắng đây là một cách tiếp cận tồi để làm việc.

3 - Đồ họa

Đây là điều lớn. Nó không liên quan đến thư viện đồ họa mà tôi đang sử dụng, nhưng là một thứ mang tính khái niệm. Trong C #, tôi đã kết hợp đồ họa với rất nhiều lớp mà tôi biết là một ý tưởng tồi tệ. Muốn làm điều đó tách rời lần này tôi đã thử một cách tiếp cận khác.

Để thực hiện đồ họa của mình, tôi đã tưởng tượng mọi thứ đồ họa liên quan trong trò chơi là một loạt các màn hình. Tức là có màn hình tiêu đề, màn hình trạng thái nhân vật, màn hình bản đồ, màn hình kho đồ, màn hình chiến đấu, màn hình GUI chiến đấu và về cơ bản tôi có thể xếp các màn hình này lên nhau khi cần thiết để tạo đồ họa trò chơi. Bất kể màn hình hoạt động là sở hữu đầu vào trò chơi.

Tôi đã thiết kế một trình quản lý màn hình sẽ đẩy và bật màn hình dựa trên đầu vào của người dùng.

Chẳng hạn, nếu bạn đang ở trên màn hình bản đồ (trình xử lý / trình hiển thị đầu vào cho Bản đồ Ngói) và nhấn nút bắt đầu, nó sẽ đưa ra lệnh gọi đến trình quản lý màn hình để đẩy màn hình Menu chính qua màn hình bản đồ và đánh dấu bản đồ màn hình để không được vẽ / cập nhật. Người chơi sẽ điều hướng xung quanh menu, sẽ đưa ra nhiều lệnh hơn cho trình quản lý màn hình khi thích hợp để đẩy màn hình mới lên ngăn xếp màn hình, sau đó bật chúng khi người dùng thay đổi màn hình / hủy bỏ. Cuối cùng, khi người chơi thoát khỏi menu chính, tôi sẽ tắt nó và quay lại màn hình bản đồ, nhận xét nó sẽ được vẽ / cập nhật và đi từ đó.

Màn hình chiến đấu sẽ phức tạp hơn. Tôi có một màn hình để làm nền, một màn hình để trực quan hóa từng bên trong trận chiến và một màn hình để trực quan hóa giao diện người dùng cho trận chiến. Giao diện người dùng sẽ nối vào các sự kiện nhân vật và sử dụng chúng để xác định khi nào cần cập nhật / vẽ lại các thành phần UI. Cuối cùng, mọi cuộc tấn công có tập lệnh hoạt hình có sẵn sẽ gọi một lớp bổ sung để tự tạo hiệu ứng trước khi bật ra khỏi ngăn xếp màn hình. Trong trường hợp này, mọi lớp đều được đánh dấu là có thể vẽ và cập nhật được và tôi có được một chồng màn hình xử lý đồ họa chiến đấu của mình.

Mặc dù tôi chưa thể khiến trình quản lý màn hình hoạt động hoàn hảo nhưng tôi nghĩ tôi có thể làm được một lúc. Câu hỏi của tôi về nó là, đây có phải là một cách tiếp cận đáng giá không? Nếu đó là một thiết kế tồi tôi muốn biết bây giờ trước khi tôi đầu tư quá nhiều thời gian để làm tất cả các màn hình tôi sẽ cần. Làm thế nào để bạn xây dựng đồ họa cho trò chơi của bạn?

Câu trả lời:


15

Nhìn chung, tôi sẽ không nói bất cứ điều gì bạn liệt kê sẽ khiến bạn loại bỏ hệ thống và bắt đầu lại. Đây là điều mà mọi lập trình viên muốn thực hiện khoảng 50-75% trong bất kỳ dự án nào họ đang thực hiện, nhưng nó dẫn đến một chu kỳ phát triển không bao giờ kết thúc và không bao giờ hoàn thành bất cứ điều gì. Vì vậy, đến cuối cùng, một số nguồn cấp dữ liệu trở lại trên mỗi phần.

  1. Đây có thể là một vấn đề nhưng thường gây khó chịu hơn bất cứ điều gì khác. Bạn đang sử dụng #pragma một lần hay #ifndef MY_HEADER_FILE_H #define MY_HEADER_FILE_H ... #endif ở đầu hoặc xung quanh các tệp .h của bạn? Bằng cách này, tệp .h chỉ tồn tại một lần trong mỗi phạm vi? Nếu bạn là như vậy, đề xuất của tôi sau đó sẽ xóa tất cả các câu lệnh #incoide và biên dịch, thêm vào những câu hỏi cần thiết để biên dịch lại trò chơi.

  2. Tôi là một fan hâm mộ của các loại hệ thống và không thấy có gì sai với nó. Sự kiện trong C # thường được thay thế bằng Hệ thống sự kiện hoặc Hệ thống nhắn tin (có thể tìm kiếm các câu hỏi ở đây để biết những điều đó để tìm thêm thông tin). Chìa khóa ở đây là giữ những điều này ở mức tối thiểu khi mọi thứ cần phải xảy ra, điều mà có vẻ như bạn đang làm như vậy nên không phải là những lo lắng tối thiểu ở đây.

  3. Điều này cũng có vẻ đúng hướng với tôi và là những gì tôi làm cho động cơ của riêng tôi, cả cá nhân và chuyên nghiệp. Điều này làm cho hệ thống menu thành một hệ thống trạng thái có menu gốc (trước khi trò chơi bắt đầu) hoặc trình phát HUD làm màn hình 'root' được hiển thị, tùy thuộc vào cách bạn thiết lập nó.

Vì vậy, để tóm tắt, tôi thấy không có gì khởi động lại xứng đáng trong những gì bạn đang chạy vào. Bạn có thể muốn một sự thay thế hệ thống Sự kiện trang trọng hơn nhưng điều đó sẽ đến kịp. Cyclic bao gồm là một trở ngại cho tất cả các lập trình viên C / C ++ liên tục phải nhảy qua, và làm việc để tách rời đồ họa tất cả có vẻ như hợp lý 'các bước tiếp theo'.

Hi vọng điêu nay co ich!


#ifdef không giúp thông tư bao gồm các vấn đề.
Vịt Cộng sản

Đã chỉ bao gồm cơ sở của tôi với hy vọng rằng sẽ ở đó trước khi theo dõi chu kỳ bao gồm. Có thể là cả một ấm cá khác khi bạn có nhiều biểu tượng được định nghĩa trái ngược với một tệp cần bao gồm một tệp bao gồm chính nó. (mặc dù từ những gì anh ấy đã mô tả nếu bao gồm trong các tệp .CPP chứ không phải các tệp .H, anh ấy sẽ ổn với hai đối tượng cơ bản biết về nhau)
James

Cảm ơn vì lời khuyên :) Rất vui khi biết tôi đang đi đúng hướng
127817

4

Sự phụ thuộc theo chu kỳ của bạn không phải là vấn đề miễn là bạn chuyển tiếp khai báo các lớp nơi bạn có thể trong các tệp tiêu đề và thực sự # bao gồm chúng trong các tệp .cpp (hoặc bất cứ điều gì).

Đối với hệ thống sự kiện, hai gợi ý:

1) Nếu bạn muốn giữ mẫu hiện đang sử dụng, hãy xem xét chuyển sang boost :: unordered_map thay vì std :: map. Ánh xạ với các chuỗi như các khóa là chậm, đặc biệt là khi .NET thực hiện một số điều hay dưới mui xe để giúp tăng tốc mọi thứ. Sử dụng unordered_map băm các chuỗi để so sánh thường nhanh hơn.

2) Cân nhắc chuyển sang một thứ mạnh hơn như tín hiệu boost ::. Nếu bạn làm điều đó, bạn có thể làm những việc tốt như làm cho các đối tượng trò chơi của mình có thể theo dõi được bằng cách lấy từ boost :: signal :: trackable và để kẻ hủy diệt chăm sóc dọn dẹp mọi thứ thay vì phải hủy đăng ký thủ công khỏi hệ thống sự kiện. Bạn cũng có thể có nhiều tín hiệu trỏ đến từng vị trí (hoặc ngược lại, tôi không nhớ danh pháp chính xác) vì vậy nó rất giống với thực hiện +=trên delegateC #. Vấn đề lớn nhất với các tín hiệu boost :: là nó phải được biên dịch, nó không chỉ là các tiêu đề, do đó tùy thuộc vào nền tảng của bạn, có thể là một nỗi đau để đứng dậy và chạy.

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.