XNA - Các lớp tĩnh từ thư viện trò chơi thực thi sau khi mở rộng đường ống nội dung, làm thế nào để tránh sự cố?


8

Được rồi, công thức theo cách này tôi chắc chắn nó nghe có vẻ mơ hồ, nhưng tôi sẽ làm hết sức để mô tả vấn đề của mình.

Đầu tiên, hãy để tôi giải thích những gì sai với mã nguồn thực sự của tôi.

Tôi đang tạo một nền tảng và đây là các thành phần hiện tại của trò chơi của tôi: Cấp độ , đang giữ lưới ô vuông của riêng mình, cũng như các ô xếp và Ngói , chỉ giữ tên của ô và chỉ mục của nó trong tấm ngói. Tất cả những thứ này đều nằm trong một dự án trò chơi XNA riêng, có tên là GameLib.

Đây là cách các cấp độ của tôi được định dạng trong một tệp văn bản:

x x . . . . .

. . . b g r .

. . . . . . .

X X X X X X X

Trường hợp .đại diện cho một ô trống,b đại diện cho một nền tảng màu xanh, Xđại diện cho một khối với một màu ngẫu nhiên, vv

Thay vì thực hiện tất cả công việc tải một cấp trong Cấp độ lớp , tôi quyết định tận dụng tiện ích mở rộng đường ống nội dung và tạo LevelContentPipelineExtension.

Ngay khi tôi bắt đầu, tôi nhanh chóng nhận ra rằng tôi không muốn mã hóa hơn 20 dòng loại này:

if (symbol == 'b')
    return new Tile(/*...*/);

Vì vậy, với hy vọng ít nhất là tập trung tất cả việc tạo nội dung vào một lớp, tôi đã tạo một lớp tĩnh, được gọi là LevelContentCreation , chứa thông tin về biểu tượng nào tạo ra cái gì và tất cả các loại công cụ này. Nó cũng giữ một danh sách tất cả các đường dẫn tài sản của các ô xếp, để tải chúng sau này. Lớp này nằm trong dự án thư viện trò chơi GameLib của tôi.

Vấn đề là, tôi đang sử dụng lớp tĩnh LevelContentCreation này trong cả lớp Cấp độ của tôi (để tải các trang tính thực tế) và phần mở rộng đường ống nội dung của tôi (để biết biểu tượng nào đại diện cho ô nào) ...

Và đây là lớp LevelContentCreation của tôi trông như thế nào:

public static class LevelContentCreation
{
    private static Dictionary<char, TileCreationInfo> AvailableTiles =
        CreateAvailableTiles();

    private static List<string> AvailableTileSheets { get; set; }

    /* ... rest of the class */
}

Bây giờ, vì lớp LevelContentCreation là một phần của thư viện trò chơi, phần mở rộng đường ống nội dung cố gắng sử dụng nó nhưng cuộc gọi đến CreatAv AvailableTiles () không bao giờ xảy ra và tôi không thể tải đối tượng Cấp độ từ đường ống nội dung.

Tôi biết điều này là do đường ống nội dung thực thi trước khi biên dịch thực tế hoặc một cái gì đó tương tự, nhưng làm thế nào tôi có thể khắc phục vấn đề?

Tôi thực sự không thể di chuyển lớp LevelContentCreation sang phần mở rộng đường ống, bởi vì sau đó là Cấp độ lớp sẽ không thể sử dụng nó ... Tôi khá lạc lõng và tôi không biết phải làm gì ... Tôi biết những điều này là những quyết định thiết kế khá tệ và tôi muốn nó nếu ai đó có thể chỉ cho tôi đi đúng hướng.

Cảm ơn bạn đã dành thời gian quý báu của bạn.

Nếu bất cứ điều gì không đủ rõ ràng hoặc nếu tôi nên cung cấp thêm thông tin / mã, xin vui lòng để lại nhận xét bên dưới.


Một chút thông tin về các dự án của tôi:

  • Trò chơi - Dự án trò chơi XNA Windows. Chứa các tham chiếu đến: Engine, GameLib và GameContent
  • GameLib - Dự án thư viện game XNA. Chứa các tham chiếu đến: Engine
  • GameContent - Dự án nội dung XNA. Chứa các tham chiếu đến: LevelContentPipelineExtension
  • Engine - Dự án thư viện game XNA. Không có tài liệu tham khảo.
  • LevelContentPipelineExtension - Mở rộng đường ống nội dung XNA. Chứa các tham chiếu đến: Engine và GameLib

Tôi không biết gì về XNA 4.0 và tôi hơi lạc lõng về cách thức hoạt động của nội dung bây giờ. Nếu bạn cần thêm thông tin, cho tôi biết.


Tôi hoàn toàn không thể đặt ngón tay vào cái gì - nhưng tôi nghĩ bạn đang thiếu một số thông tin. Bạn có thể giải thích chính xác những dự án bạn có (trò chơi, thư viện trò chơi, mở rộng đường ống nội dung, dự án nội dung) và những gì tham khảo những gì?
Andrew Russell

Chắc chắn, tôi biết nó sẽ không đủ rõ ràng. Chỉnh sửa ngay bây giờ.
Jesse Emond

Bằng cách này, mọi thứ có vẻ ổn ngoại trừ một ngoại lệ được ném vào thời gian biên dịch trong phần mở rộng đường ống. Ngoại lệ thực sự cho tôi biết rằng nội dung của tệp cấp độ không thể được chuyển đổi, vì các ký hiệu có sẵn chưa được tải trong tệp LevelContentCreation của tôi. Tôi thậm chí đã kiểm tra bằng cách đặt một ngoại lệ trong cả phần mở rộng đường ống và lớp tĩnh và ngoại lệ của phần mở rộng đường ống được đưa ra trước tiên, vì vậy vấn đề thực sự có thể đến từ đó và tôi cần phải cơ cấu lại mã của mình.
Jesse Emond

Câu trả lời:


11

Trong trường hợp bạn đang sử dụng singletons, lớp tĩnh hoặc biến toàn cục, 99% thời gian những gì bạn thực sự nên sử dụng là một dịch vụ trò chơi . Các dịch vụ trò chơi được sử dụng khi bạn muốn một phiên bản của một lớp có sẵn cho tất cả các lớp khác, nhưng sự kết hợp chặt chẽ của các tùy chọn khác mang đến cho bạn một hương vị hài hước. Các dịch vụ cũng không có vấn đề khởi tạo mà bạn đã thấy, chúng có thể tái sử dụng nhiều hơn và chúng có thể bị chế giễu (nếu bạn quan tâm đến kiểm tra đơn vị tự động) .

Để đăng ký một dịch vụ, bạn cần cung cấp cho lớp của mình giao diện riêng. Sau đó chỉ cần gọi Game.Services.AddService(). Để sau này sử dụng dịch vụ đó trong một lớp khác, hãy gọi Game.Services.GetService().

Trong trường hợp của bạn, lớp của bạn sẽ trông giống như thế này:

public interface ILevelContentCreation
{
    void SomeMethod();
    int SomeProperty { get; }
}

public class LevelContentCreation : ILevelContentCreation
{
    private Dictionary<char, TileCreationInfo> availableTiles =
        CreateAvailableTiles();

    private List<string> AvailableTileSheets { get; set; }

    public void SomeMethod() { /*...*/ }
    public int SomeProperty { get; private set; }

    /* ... rest of the class ... */
}

Tại một số thời điểm khi bắt đầu chương trình, bạn phải đăng ký dịch vụ của mình Game.Services. Tôi thích làm điều này khi bắt đầu LoadContent(), nhưng một số người thích đăng ký từng dịch vụ trong hàm tạo của lớp đó.

/* Main game */
protected override void LoadContent()
{
    RegisterServices();

    /* ... */
}

private void RegisterServices()
{
    Game.Services.AddService(typeof(ILevelContentCreation), new LevelContentCreation());
}

Bây giờ bất cứ khi nào bạn muốn truy cập LevelContentCreationlớp của mình trong bất kỳ lớp nào khác, bạn chỉ cần làm điều này:

ILevelContentCreation levelContentCreation = (ILevelContentCreation) Game.Services.GetService(typeof(ILevelContentCreation));
levelContentCreation.SomeMethod(); //Tada!

Một lưu ý phụ, mô hình này được gọi là Inversion of Control (IoC) và cũng được sử dụng rộng rãi ngoài sự phát triển của XNA. Khung phổ biến nhất cho IoC trong .Net là Ninject , có cú pháp đẹp hơn các phương thức Game.Service:

Bind<ILevelContentCreation>().To<LevelContentCreation>(); //Ninject equivalent of Services.AddService()
ILevelContentCreation levelContentCreation = kernel.Get<ILevelContentCreation>(); //Ninject equivalent of Services.GetService()

Nó cũng hỗ trợ tiêm phụ thuộc , một dạng IoC phức tạp hơn một chút, nhưng hoạt động tốt hơn nhiều.

[Chỉnh sửa] Tất nhiên, bạn cũng có thể nhận được cú pháp hay đó với XNA:

static class ServiceExtensionMethods
{
    public static void AddService<T>(this GameServiceContainer services, T service)
    {
        services.AddService(typeof(T), service);
    }

    public static T GetService<T>(this GameServiceContainer services)
    {
        return (T)services.GetService(typeof(T));
    }
}

1
Wow, mặc dù tôi đã khắc phục vấn đề của mình, điều này sẽ giúp mã của tôi sạch hơn rất nhiều. Cảm ơn bạn đã giúp đỡ, chấp nhận câu trả lời của bạn.
Jesse Emond

1

Tôi thực sự tìm thấy một cách để khắc phục vấn đề: Tôi đã loại bỏ lớp tĩnh và biến nó thành một lớp bình thường. Bằng cách này, tôi chắc chắn rằng các câu lệnh đúng được thực thi đúng lúc.

Tôi ước tôi đã không lãng phí thời gian của bạn như vậy nhưng tôi thực sự có ý tưởng làm như vậy bằng cách đọc các bình luận / câu trả lời.

Dù sao cũng cảm ơn bạn.


Một lưu ý khác không liên quan đến câu trả lời của tôi ở trên / bên dưới: nếu bạn thấy mình đang làm if/ các switchcâu lệnh chỉ trả về các loại khác nhau, bạn có thể muốn các giá trị đó là một phần của lớp thay thế. Trong trường hợp của bạn, tôi sẽ tạo một TileTypelớp chứa thông tin (kết cấu, lưu tệp-thư, v.v.) về từng loại ô. Sau đó, để nhận được một TileTypechữ cái, chỉ cần tìm kiếm trong danh sách của bạn TileTypeshoặc nếu bạn quan tâm đến hiệu suất, hãy lưu trữ một tĩnh Dictionary<char, TileType>trong TileTypelớp (bạn có thể tự động điền nó vào hàm TileTypetạo).
BlueRaja - Daniel Pflughoeft

Sau đó, mỗi Tile(lớp đại diện cho một ô hiển thị trên màn hình) sẽ tham chiếu một ô duy nhấtTileType . Nếu đủ các ô của bạn có hành vi duy nhất yêu cầu mã đặc biệt, bạn sẽ muốn sử dụng phân cấp lớp thay thế.
BlueRaja - Daniel Pflughoeft

0

Hãy xem cái này: http://msdn.microsoft.com/en-us/l Library / bb447745.aspx

Đối với bố cục cấp độ, tôi chỉ cần đặt một hàm bên trong lớp Cấp độ của mình được gọi là "LoadFromFile (tên chuỗi)". Bởi vì nếu bạn muốn thêm hỗ trợ cho các bản đồ tùy chỉnh trong trò chơi của mình sau này, bạn sẽ phải lưu và tải các cấp độ từ định dạng tệp của riêng bạn.

Bạn vẫn có thể tạo một trình nhập nội dung, để tải từ các tài sản được bao gồm và cũng từ các tệp do người dùng cung cấp. Xem trang này để biết thêm thông tin về cách tạo nhà nhập khẩu

http://msdn.microsoft.com/en-us/l Library / bb447743.aspx


Cảm ơn câu trả lời của bạn. Vấn đề của tôi không thực sự đến từ việc tạo phần mở rộng đường ống nội dung, nhưng từ thực tế là lớp tạo mức của tôi được sử dụng bởi cả lớp Cấp độ và phần mở rộng đường ống nội dung của tôi, vì vậy mã đường ống được thực thi trước khi khai báo lớp tĩnh và không có gì được khởi tạo ở đó
Jesse Emond
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.