Genişletilebilir varlık yükleme sistemini nasıl yapılandırmalıyım?


19

Java'da bir hobi oyun motoru için, basit ama esnek bir varlık / kaynak yöneticisini kodlamak istiyorum. Varlıklar sesler, görüntüler, animasyon, modeller, dokular, vb. Birkaç saatlik tarama ve bazı kod deneylerinden sonra hala bu şeyi nasıl tasarlayacağımdan emin değilim.

Özellikle, nasıl belirli varlık türleri yüklendiğini ve varlıkların yüklendiği yerden soyutlar böylece Yöneticisi nasıl bir şekilde tasarım arıyorum. Programın geri kalanı hakkında bilmek gerek kalmadan hem dosya sistemini hem de RDBMS depolama alanını desteklemek istiyorum. Benzer şekilde, XML olan bir animasyon açıklaması öğesi (FPS, oluşturulacak çerçeveler, hareketli grafik görüntüsüne referans vb.) Eklemek istiyorum. Bir XML dosyası bulma ve okuma ve AnimationAssetbu bilgileri içeren bir sınıf oluşturma ve döndürme işleviyle bunun için bir sınıf yazabilmeliyim . Veri odaklı bir tasarım arıyorum .

Ben birçok bilgi bulabilirsiniz neyi ancak üzerinde, bir varlık yöneticisi yapmalıyım nasıl bunu yapmak için. İlgili jenerikler, bazı sınıfların basamaklı veya bazı yardımcı sınıflarla sonuçlanıyor gibi görünmektedir. Ancak kişisel bir hack ya da fikir birliği gibi görünmeyen net bir örnek görmedim.

Yanıtlar:


23

Bir varlık yöneticisi düşünmeyerek başlardım . Mimarinizi gevşek tanımlanmış terimlerle ("yönetici" gibi) düşünmek, halının altında birçok ayrıntıyı zihinsel olarak süpürmenize izin verir ve sonuç olarak bir çözüme yerleşmek daha zor hale gelir.

Temel kaynak deposunu soyutlayan ve desteklenen tür kümesinin genişletilebilirliğine izin veren bir kaynak yükleme mekanizması oluşturmakla ilgili görünen özel gereksinimlerinize odaklanın. Örneğin, önceden yüklenmiş kaynakların önbelleğe alınmasıyla ilgili gerçekten hiçbir şey yok - bu iyi, çünkü tek sorumluluk ilkesine uygun olarak muhtemelen bir varlık önbelleği ayrı bir varlık olarak oluşturmalı ve iki arayüzü başka bir yerde toplamalısınız , uygun.

Özel endişenizi gidermek için yükleyicinizi herhangi bir varlığın yüklenmesini değil, belirli varlık türlerini yüklemeye uyarlanmış arayüzlere karşı sorumluluğu devredecek şekilde tasarlamalısınız. Örneğin:

interface ITypeLoader {
  object Load (Stream assetStream);
}

Bu arabirimi uygulayan yeni sınıflar oluşturabilirsiniz; her yeni sınıf bir akıştan belirli bir veri türünü yüklemek üzere uyarlanmıştır. Bir akış kullanarak, tip yükleyici, depolamadan bağımsız ortak bir arabirime karşı yazılabilir ve diskten veya veritabanından yüklenmek için sabit kodlanması gerekmez; bu bile varlıklarınızı ağ akışlarından yüklemenize izin verir (bu, oyununuz bir konsolda çalışırken ve varlıkların ağa bağlı bir PC'de düzenleme araçlarınız çalışırken varlıkların sıcak olarak yeniden yüklenmesini uygulamada çok yararlı olabilir).

Ana varlık yükleyicinizin bu türe özgü yükleyicileri kaydedebilmesi ve izleyebilmesi gerekir:

class AssetLoader {
  public void RegisterType (string key, ITypeLoader loader) {
    loaders[key] = loader;
  }

  Dictionary<string, ITypeLoader> loaders = new Dictionary<string, ITypeLoader>();
}

Burada kullanılan "anahtar" istediğiniz gibi olabilir - ve bir dize olması gerekmez, ancak bunlarla başlamak kolaydır. Anahtar, bir kullanıcının belirli bir varlığı nasıl tanımlamasını beklediğinize bağlı olacaktır ve uygun yükleyiciyi aramak için kullanılacaktır. Uygulamanın bir dosya sistemi veya veritabanı kullanıyor olabileceği gerçeğini gizlemek istediğiniz için, kullanıcılarınızın dosya sistemi yolu veya benzeri bir şeyle varlıklara atıfta bulunmasını sağlayamazsınız.

Kullanıcılar, asgari düzeyde bilgiye sahip bir varlığa başvurmalıdır. Bazı durumlarda, yalnızca bir dosya adı tek başına yeterli olur, ancak her şeyin çok açık olması için bir tür / ad çifti kullanmanın genellikle arzu edilir olduğunu gördüm. Bu nedenle, bir kullanıcı animasyon XML dosyalarınızdan birinin adlandırılmış bir örneğini olarak belirtebilir "AnimationXml","PlayerWalkCycle".

Burada, AnimationXmlaltında kaydolduğunuz AnimationXmlLoaderve uyguladığınız anahtar olacaktır IAssetLoader. Açıkçası, PlayerWalkCyclebelirli bir varlığı tanımlar. Bir tür adı ve kaynak adı verildiğinde, varlık yükleyiciniz kalıcı depolama alanını söz konusu varlığın ham baytları için sorgulayabilir. Burada maksimum genelliğe gideceğimizden, bunu yükleyiciyi oluştururken bir depolama erişimi aracı geçirerek uygulayabilir ve depolama ortamını daha sonra bir akış sağlayabilecek herhangi bir şeyle değiştirmenize izin verir:

interface IAssetStreamProvider {
  Stream GetStream (string type, string name);
}

class AssetLoader {
  public AssetLoader (IAssetStreamProvider streamProvider) {
    provider = streamProvider;
  }

  object LoadAsset (string type, string name) {
    var loader = loaders[type];
    var stream = provider.GetStream(type, name);

    return loader.Load(stream);
  }

  public void RegisterType (string type, ITypeLoader loader) {
    loaders[type] = loader;
  }

  IAssetStreamProvider provider;
  Dictionary<string, ITypeLoader> loaders = new Dictionary<string, ITypeLoader>();
}

Çok basit bir akış sağlayıcısı, belirtilen bir varlık kök dizininde adlandırılmış bir alt dizini arar ve adlandırılan typedosyanın ham baytlarını namebir akışa yükler ve döndürür.

Kısacası, burada sahip olduğunuz bir sistem:

  • Bir çeşit arka uç deposundan (disk, veritabanı, ağ akışı, her neyse) ham baytların nasıl okunacağını bilen bir sınıf var.
  • Ham bayt akışını belirli bir kaynağa dönüştürmeyi ve geri döndürmeyi bilen sınıflar vardır.
  • Gerçek "varlık yükleyiciniz" sadece yukarıda belirtilenlerin bir koleksiyonuna sahiptir ve akış sağlayıcısının çıktısının türe özgü yükleyiciye nasıl bağlanacağını ve böylece somut bir varlığın nasıl üretileceğini bilir. Akış sağlayıcısını ve türe özgü yükleyicileri yapılandırmanın yollarını açığa çıkararak, gerçek varlık yükleyici kodunu değiştirmek zorunda kalmadan istemciler (veya kendiniz) tarafından genişletilebilen bir sisteminiz olur.

Bazı uyarılar ve son notlar:

  • Yukarıdaki kod temel olarak C # 'dır, ancak minimum çaba ile hemen hemen her dile tercüme edilmelidir. Bunu kolaylaştırmak için, hata denetimi veya doğru kullanımı IDisposableve doğrudan diğer dillerde uygulanamayan diğer deyimler gibi birçok şeyi atladım. Bunlar okuyucuya ödev olarak bırakılıyor.

  • Benzer şekilde, somut varlığı objectyukarıdaki gibi döndürdüm , ancak isterseniz daha spesifik bir nesne türü üretmek için jenerikler veya şablonlar veya her şeyi kullanabilirsiniz (çalışmanız iyi olur).

  • Yukarıdaki gibi, burada önbellekleme ile hiç ilgilenmiyorum. Ancak, önbelleklemeyi kolayca ve aynı genelliğe ve yapılandırılabilirliğe ekleyebilirsiniz. Deneyin ve görün!

  • Bunu yapmak için çok ve çok ve birçok yol var ve kesinlikle tek bir yol ya da fikir birliği yok, bu yüzden bir tane bulamadınız. Ben ağrılı bir uzun kod duvar bu cevap dönmeden genelinde belirli noktaları almak için yeterli kod sağlamaya çalıştım. Oldukça fazla uzun. Açıklayıcı sorularınız varsa, yorum yapmaktan veya beni sohbette bulmaktan çekinmeyin .


1
Çözümü sadece veri odaklı bir tasarıma değil, aynı zamanda veri odaklı bir şekilde düşünmeye
Patrick Hughes

Çok güzel ve derinlemesine bir cevap. Sorumu nasıl yorumladığınızı ve bu kadar zayıf formüle ederken bilmem gerekenleri tam olarak anlattığınızı seviyorum. Teşekkürler! Şans eseri, beni Akımlar ile ilgili bazı kaynaklara yönlendirebilir misiniz?
user8363

Bir "akış" sadece bayt veya verinin bir dizisidir (potansiyel olarak belirlenebilir sonu olmayan). Özellikle C # 's Stream düşünüyordum , ama muhtemelen Java'nın akış sınıfları ile daha fazla ilgileniyorsunuz - uyarılmakla birlikte çok fazla Java bilmiyorum, bu yüzden kullanmak için ideal bir sınıf olmayabilir.

Akışlar genellikle durumsaldır, belirli bir akış nesnesinin genellikle akış içinde geçerli bir okuma veya yazma konumuna sahip olması ve üzerinde gerçekleştirdiğiniz herhangi bir IO bu konumdan gerçekleşir - bu yüzden onları yukarıdaki varlık arabirimlerine girdi olarak kullandım, çünkü aslında "işte bazı ham veriler ve nereden okumaya, nereden okumaya ve işinizi yapmaya nereden başlayacağınızı" söylüyorlar.

Bu yaklaşım onur hem temel ilkeleri bazı KATI ve OOP . Bravo.
Adam Naylor
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.