Tôi muốn thoát khỏi mẫu thiết kế tạo mọi thứ tĩnh và toàn cầu của mình, nhưng làm thế nào?


11

Tôi đang tạo một trình thu thập dungeon nhỏ trong không gian và tôi muốn nghe một số lời khuyên về cách làm cho phần phụ trợ của động cơ đẹp hơn. Về cơ bản, hiện tại mọi thứ đều dựa trên một đống rác của các nhà quản lý:

  • BackgroundManager: có AddBackground(image, parallax)phương pháp để tạo hiệu ứng nền mát mẻ.
  • ConfigManager: đọc / tạo tệp cấu hình và cũng giữ dữ liệu đọc từ tệp cấu hình đó.
  • DrawManager: có Draw(sprite)phương pháp để vẽ các thứ lên màn hình và những thứ như SetView(view), GetView()v.v.
  • EntityManager: chứa tất cả các thực thể hoạt động và có các phương thức để thêm, xóa và tìm các thực thể.
  • DungeonManager (thực sự gọi là GridManager, nhưng điều này là vì đơn giản): có phương pháp thích GenerateDungeon(), PlaceEnemies(), PlaceFinish(), vv

Bây giờ tôi thậm chí không đi được nửa đường trong danh sách tất cả các nhà quản lý của mình, vì vậy tôi nghĩ rằng điều này đã phải thay đổi. Trò chơi của tôi cũng dựa trên Màn hình, điều này gây khó chịu hơn vì tôi không cần một nửa những thứ mà người quản lý của tôi đang quản lý, ví dụ: menu chính (tôi không cần vật lý / thực thể / dungeon trong menu chính của mình !)

Bây giờ tôi nghĩ về việc làm cho các trình quản lý không tĩnh và đưa ScreenMainGame của tôi làm ví dụ cho tất cả các trình quản lý dành riêng cho trò chơi. Nhưng điều đó làm cho việc gọi / nhận bất cứ thứ gì liên quan đến người quản lý trở thành một mớ hỗn độn ... ví dụ, nếu tôi muốn rút ngục tối của mình từ bất kỳ nơi nào không có ScreenMainGame.Draw(), tôi phải làm điều này ...

((ScreenMainGame)ScreenManager.CurrentScreen).GridManager.Draw()

Điều đó thực sự xấu xí.

Vì vậy, các nhà phát triển trò chơi đồng nghiệp, có ai trong các bạn có ý tưởng làm thế nào để giải quyết mớ hỗn độn này không? Tôi sẽ được đánh giá cao!


Tôi không chắc chắn chính xác làm thế nào để giúp bạn, nhưng tôi khuyên bạn nên đọc một cuốn sách hay về các mẫu thiết kế. Tôi đã đọc các mẫu thiết kế đầu tiên và nó rất dễ hiểu. Các ví dụ có trong Java, nhưng tôi nghĩ rằng nó sẽ đủ gần với C # để giúp bạn. Xin lỗi nếu đề nghị của tôi là quá mới.
Amplify91

Bạn đang sử dụng gì để giao tiếp với card đồ họa? XNA? SlimDX?
BlueRaja - Daniel Pflughoeft

@BlueRaja: Tôi đang sử dụng SFML.NET.
Dlaor

Trong trường hợp đó, bạn chỉ cần sử dụng các phương pháp thông thường để xử lý các vấn đề này trong C #: Xem liên kết cuối cùng của tôi bên dưới, cũng như tiêm phụ thuộc là gì?
BlueRaja - Daniel Pflughoeft

Câu trả lời:


10

Một động cơ dựa trên thành phần thì sao?

Bạn sẽ có một lớp chính được đặt tên Engine, trong đó sẽ giữ một danh sách GameScreens, trong đó chính họ sẽ giữ một danh sáchComponents .

Động cơ có một Updatevà một Drawphương pháp và cả lời kêu gọi GameScreen's UpdateDrawphương pháp, mà mình đi qua tất cả các thành phần và cuộc gọi UpdateDraw.

Trình bày như vậy, tôi đồng ý rằng nó có vẻ như một thiết kế nghèo nàn và lặp đi lặp lại. Nhưng tin tôi đi, mã của tôi trở nên sạch hơn rất nhiều khi sử dụng cách tiếp cận dựa trên thành phần so với tất cả các lớp quản lý cũ của tôi .

Việc duy trì mã như vậy cũng đơn giản hơn nhiều, vì bạn chỉ cần trải qua một hệ thống phân cấp lớp lớn và không phải tìm kiếm BackgroundManagertất cả các nền tảng cụ thể khác nhau. Bạn chỉ có một ScrollingBackground, ParallaxBackground, StaticBackground, vv mà tất cả bắt nguồn từ một Backgroundlớp.

Cuối cùng, bạn sẽ xây dựng một công cụ khá vững chắc để bạn có thể sử dụng lại tất cả các dự án của mình với rất nhiều thành phần và phương thức trợ giúp được sử dụng thường xuyên (ví dụ FrameRateDisplayernhư một tiện ích gỡ lỗi, một Spritelớp như một sprite cơ bản với kết cấu và phương thức mở rộng cho vectơ và tạo số ngẫu nhiên).

Bạn sẽ không còn có một BackgroundManagerlớp nữa, mà là một Backgroundlớp sẽ tự quản lý.

Khi trò chơi của bạn bắt đầu, tất cả những gì bạn phải làm là về cơ bản:

// when declaring variables:
Engine engine;

// when initializing:
engine = new Engine();
engine.Initialize();
engine.LoadContent();
engine.AddGameScreen(new MainMenuScreen());

// when updating:
engine.Update();

// when drawing:
engine.Draw();

Và đó là mã bắt đầu trò chơi của bạn.

Sau đó, cho màn hình menu chính:

class MainMenuScreen : MenuScreen // where MenuScreen derives from the GameScreen class
{
    const int ENEMY_COUNT = 10;

    StaticBackground background;
    Player player;
    List<Enemy> enemies;

    public override void Initialize()
    {
        background = new StaticBackground();
        player = new Player();
        enemies = new List<Enemy>();

        base.AddComponent(background); // defined within the GameScreen class
        base.AddComponent(player);
        for (int i = 0; i < ENEMY_COUNT; ++i)
        {
            Enemy newEnemy = new Enemy();
            enemies.Add(newEnemy);
            base.AddComponent(newEnemy);
        }
    }
}

Bạn nhận được các ý tưởng chung.

Bạn cũng sẽ giữ tham chiếu của Enginetất cả các GameScreenlớp của mình , để có thể thêm màn hình mới ngay cả trong một GameScreenlớp (ví dụ: khi người dùng nhấp vào nút StartGame trong khi bạn MainMenuScreen, bạn có thể chuyển sangGameplayScreen ).

Điều tương tự cũng xảy ra với Componentlớp: nó nên giữ tham chiếu của cha mẹ của nó GameScreen, để có cả quyền truy cập vào Enginelớp và cha mẹ của nó GameScreenđể thêm các thành phần mới (ví dụ: bạn có thể tạo một lớp liên quan đến HUD được gọi là DrawableButtonchứa một DrawableTextthành phần và StaticBackgroundthành phần).

Bạn thậm chí có thể áp dụng các mẫu thiết kế khác sau đó, như "mẫu thiết kế dịch vụ" (không chắc chắn về tên chính xác) nơi bạn có thể giữ các dịch vụ hữu ích khác nhau trong Enginelớp của mình (bạn chỉ cần giữ một danh sách IServices và để các lớp khác tự thêm dịch vụ ). ví dụ tôi sẽ giữ mộtCamera2D thành phần trên tất cả các dự án của tôi như một dịch vụ để áp dụng chuyển đổi của nó khi vẽ các thành phần khác. Điều này tránh phải vượt qua nó như là một tham số ở khắp mọi nơi.

Tóm lại, chắc chắn có thể có những thiết kế khác tốt hơn cho một động cơ, nhưng tôi thấy động cơ được đề xuất bởi liên kết này rất thanh lịch, cực kỳ dễ bảo trì và có thể tái sử dụng. Cá nhân tôi muốn giới thiệu ít nhất là thử nó.


1
XNA hỗ trợ tất cả những thứ vượt trội này thông qua GameComponents và các dịch vụ. Không cần một Enginelớp học riêng .
BlueRaja - Daniel Pflughoeft

+1 cho câu trả lời này. Ngoài ra, sẽ có ý nghĩa khi đặt bản vẽ trong một luồng riêng biệt từ luồng cập nhật trò chơi?
f20k

1
@BlueRaja - DannyPflughoeft: Thật vậy nhưng tôi cho rằng XNA không được sử dụng nên tôi đã giải thích thêm một chút về cách thực hiện.
Jesse Emond

@ f20k: Vâng, khá giống cách mà XNA làm. Tôi không biết làm thế nào nó được thực hiện mặc dù. = / Phải có một bài viết ở đâu đó trên internet về cách làm. :)
Jesse Emond

Vâng, tôi không sử dụng XNA nhưng đây có vẻ là một thay thế tốt cho những gì tôi đang làm. Tôi hiện đang đọc cuốn sách "Các mẫu thiết kế đầu tiên" để xem liệu có thêm lựa chọn nào khác không. Chúc mừng!
Dlaor


-3

Chỉ vì chúng là tĩnh hoặc toàn cầu không có nghĩa là chúng phải luôn được tải / khởi tạo. Sử dụng mẫu Singleton và cho phép người quản lý được tự do và tải lại theo yêu cầu.


4
Một singleton chỉ là một mặt tiền cho vấn đề tiềm ẩn mà OP đang cố gắng giải quyết ở đâ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.