XNA - Kelas statis dari perpustakaan game yang mengeksekusi setelah ekstensi pipa konten, bagaimana cara menghindari masalah?


8

Baiklah, dirumuskan dengan cara ini saya yakin kedengarannya tidak jelas, tetapi saya akan melakukan yang terbaik untuk menggambarkan masalah saya.

Pertama, izinkan saya menjelaskan apa yang salah dengan kode sumber asli saya.

Saya membuat platformer, dan di sini adalah komponen saat ini dari permainan saya: Level , yang memegang kotak ubin sendiri dan juga lembar ubin dan Ubin , yang hanya menyimpan nama lembar ubin dan indeksnya di dalam lembaran genteng. Semua ini berada dalam proyek game XNA yang terpisah, bernama GameLib.

Berikut adalah bagaimana level saya diformat dalam file teks:

x x . . . . .

. . . b g r .

. . . . . . .

X X X X X X X

Di mana .mewakili ubin kosong, bmewakili platform biru, Xmewakili blok dengan warna acak, dll.

Alih-alih melakukan semua pekerjaan memuat level dalam kelas Level , saya memutuskan untuk mengambil keuntungan dari ekstensi pipa konten dan membuat LevelContentPipelineExtension.

Segera setelah saya mulai, saya segera menyadari bahwa saya tidak ingin kode-keras 20+ baris semacam itu:

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

Jadi, dengan harapan setidaknya memusatkan semua pembuatan konten ke dalam satu kelas, saya membuat kelas statis, yang disebut LevelContentCreation , yang menyimpan informasi tentang simbol mana yang menciptakan apa dan semua jenis barang ini. Ia juga menyimpan daftar semua jalur aset lembaran ubin, untuk memuatnya nanti. Kelas ini ada dalam proyek perpustakaan game GameLib saya.

Masalahnya adalah, saya menggunakan kelas statis LevelContentCreation ini di kedua kelas Tingkat saya (untuk memuat lembaran ubin yang sebenarnya) dan ekstensi pipa konten saya (untuk mengetahui simbol mana yang mewakili ubin apa) ...

Dan inilah tampilan kelas LevelContentCreation saya :

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

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

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

Sekarang, karena kelas LevelContentCreation adalah bagian dari perpustakaan permainan, ekstensi pipa konten mencoba menggunakannya tetapi panggilan ke CreateAvailableTiles () tidak pernah terjadi dan saya tidak dapat memuat objek Level dari pipa konten.

Saya tahu ini karena pipa konten dijalankan sebelum kompilasi yang sebenarnya atau sesuatu seperti itu, tetapi bagaimana saya bisa memperbaiki masalahnya?

Saya tidak bisa benar-benar memindahkan kelas LevelContentCreation ke ekstensi pipa, karena kemudian kelas Level tidak akan dapat menggunakannya ... Saya sangat tersesat dan saya tidak tahu harus berbuat apa ... Saya tahu ini adalah keputusan desain yang sangat buruk, dan saya ingin jika seseorang dapat mengarahkan saya ke arah yang benar.

Terima kasih atas waktu berharga Anda.

Jika ada sesuatu yang tidak cukup jelas atau jika saya harus memberikan lebih banyak informasi / kode, silakan tinggalkan komentar di bawah ini.


Sedikit informasi lebih lanjut tentang proyek saya:

  • Game - proyek game XNA Windows. Berisi referensi ke: Engine, GameLib dan GameContent
  • GameLib - proyek perpustakaan game XNA. Berisi referensi ke: Engine
  • GameContent - proyek konten XNA. Berisi referensi ke: LevelContentPipelineExtension
  • Engine - proyek perpustakaan game XNA. Tidak ada referensi.
  • LevelContentPipelineExtension - ekstensi pipa konten XNA. Berisi referensi ke: Engine dan GameLib

Saya tahu hampir tidak ada tentang XNA 4.0 dan saya sedikit bingung tentang bagaimana konten bekerja sekarang. Jika Anda memerlukan informasi lebih lanjut, beri tahu saya.


Saya tidak tahu apa - tetapi saya pikir Anda kehilangan beberapa informasi. Bisakah Anda menjelaskan dengan tepat proyek apa yang Anda miliki (game, perpustakaan game, ekstensi pipa konten, proyek konten) dan referensi apa?
Andrew Russell

Tentu, saya tahu itu tidak akan cukup jelas. Mengedit sekarang.
Jesse Emond

By the way semuanya tampak baik-baik saja kecuali bahwa pengecualian dilemparkan pada waktu kompilasi dalam ekstensi pipa. Pengecualian sebenarnya memberitahu saya bahwa konten file level tidak dapat dikonversi, karena simbol yang tersedia belum dimuat dalam file LevelContentCreation saya. Saya bahkan menguji dengan meletakkan pengecualian pada ekstensi pipa dan kelas statis dan pengecualian ekstensi pipa dilempar terlebih dahulu, jadi masalahnya mungkin benar-benar datang dari sana dan saya harus merestrukturisasi kode saya.
Jesse Emond

Jawaban:


11

Dalam kasus di mana Anda menggunakan lajang, kelas statis, atau variabel global, 99% waktu yang seharusnya Anda gunakan adalah layanan game . Layanan gim digunakan ketika Anda menginginkan satu instance kelas tersedia untuk semua kelas lain, tetapi penggabungan ketat dari opsi lain memberi Anda rasa lucu. Layanan juga tidak memiliki masalah instantiasi yang Anda lihat, mereka lebih dapat digunakan kembali, dan mereka dapat diejek (jika Anda peduli dengan pengujian unit otomatis) .

Untuk mendaftarkan layanan, Anda harus memberikan antarmuka sendiri pada kelas Anda. Lalu panggil saja Game.Services.AddService(). Untuk menggunakan layanan itu nanti di kelas lain, hubungi Game.Services.GetService().

Dalam kasus Anda, kelas Anda akan terlihat seperti ini:

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 ... */
}

Pada titik tertentu di awal program, Anda harus mendaftarkan layanan Anda Game.Services. Saya lebih suka melakukan ini di awal LoadContent(), tetapi beberapa orang lebih suka mendaftarkan setiap layanan di konstruktor kelas itu.

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

    /* ... */
}

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

Sekarang kapan pun Anda ingin mengakses LevelContentCreationkelas Anda di kelas lain, Anda cukup melakukan ini:

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

Sebagai catatan, paradigma ini dikenal sebagai Inversion of Control (IoC), dan banyak digunakan di luar pengembangan XNA juga. Kerangka kerja paling populer untuk IoC di. Net adalah Ninject , yang memiliki sintaksis lebih bagus daripada metode Game.Services:

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

Ini juga mendukung injeksi ketergantungan , suatu bentuk IoC yang sedikit lebih kompleks, tetapi jauh lebih baik untuk digunakan.

[Sunting] Tentu saja, Anda juga bisa mendapatkan sintaks yang bagus dengan 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, meskipun saya sudah memperbaiki masalah saya, ini hanya akan membuat kode saya jauh lebih bersih. Terima kasih atas bantuannya, menerima jawaban Anda.
Jesse Emond

1

Saya benar-benar menemukan cara untuk memperbaiki masalah: Saya menghapus kelas statis dan menjadikannya kelas normal. Dengan cara ini saya yakin pernyataan yang tepat akan dieksekusi pada waktu yang tepat.

Saya berharap saya tidak menyia-nyiakan waktu Anda seperti itu tetapi saya benar-benar mendapat ide untuk melakukannya dengan membaca komentar / jawaban.

Terima kasih.


Catatan lain yang tidak terkait dengan jawaban saya di atas / di bawah ini: jika Anda menemukan diri Anda melakukan if/ switchpernyataan yang hanya mengembalikan tipe yang berbeda, Anda mungkin ingin nilai-nilai itu menjadi bagian dari kelas. Dalam kasus Anda, saya akan membuat TileTypekelas, yang berisi informasi (tekstur, simpan-file-surat, dll) tentang setiap jenis ubin. Kemudian untuk mendapatkan TileTypedari surat, cukup cari melalui daftar Anda TileTypes, atau jika Anda khawatir tentang kinerja, simpan statis Dictionary<char, TileType>di TileTypekelas (Anda dapat mengisinya secara otomatis di TileTypekonstruktor).
BlueRaja - Danny Pflughoeft

Kemudian setiap Tile(kelas yang mewakili ubin tunggal ditampilkan di layar) akan merujuk satu TileType. Jika cukup ubin Anda memiliki perilaku unik yang memerlukan kode khusus, Anda akan ingin menggunakan hierarki kelas.
BlueRaja - Danny Pflughoeft

0

Lihat ini: http://msdn.microsoft.com/en-us/library/bb447745.aspx

Untuk tata letak level saya baru saja meletakkan fungsi di dalam kelas Level saya yang disebut "LoadFromFile (nama string)". Karena jika Anda ingin menambahkan dukungan untuk peta khusus di game Anda nanti, Anda harus menyimpan dan memuat level dari format file Anda sendiri.

Anda juga masih dapat membuat importir konten, untuk memuat dari aset yang disertakan dan juga dari file yang disediakan pengguna. Lihat halaman ini untuk informasi lebih lanjut tentang cara membuat importir

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


Terima kasih atas jawaban anda. Masalah saya sebenarnya bukan berasal dari pembuatan ekstensi pipa konten, tetapi dari fakta bahwa kelas pembuatan level saya digunakan baik oleh kelas Level saya dan ekstensi pipa konten saya, sehingga kode pipa dieksekusi sebelum deklarasi kelas statis dan tidak ada yang diinisialisasi di sana.
Jesse Emond
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.