MonoGameで外部のXNBファイルを読み込む
2017年3月9日 (木) 10:33時点における103.22.200.108 (トーク)による版
MonoGameで作っているゲームに起動時データ更新を実装し、プログラム以外の画像や音楽などだけであれば自動更新するようにしたいわけだ。スマホのゲームによくあるアレである。むしろプログラムの細かい部分もNLuaに投げるようにしてしまいたい。
そうなると外部から各種データを読み込む必要が出てくる。 しかしMonoGameで扱う各種データの基本はプログラムに組み込まれた「Content」である。
Xamarin.Android
MonoGameのソースコードをざっと見た感じ、Xamarin.AndroidではContentManagerクラスでアセット以外のファイルへはアクセスできないようだ。ContentManagerクラスのソースコードをみると幸いにもvirtualやらがチラホラ見受けられ継承してoverrideする前提の構造になっている。なので細かいことは考えずにoverrideしてしまえと考えた。
ついでに各種パラメータを保存したcsvやjsonなどのxnb以外のファイルも同様の手順で読み込めるように、xnbパーサを経由しない裏口も用意すると捗った。
using System;
using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;
public class ExternalContentManager : Microsoft.Xna.Framework.Content.ContentManager
{
public ExternalContentManager(IServiceProvider serviceProvider, string rootDirectory)
: base(serviceProvider, rootDirectory)
{
}
public ExternalContentManager(IServiceProvider serviceProvider)
: base(serviceProvider)
{
}
protected override Stream OpenStream(string assetName)
{
string assetPath;
if (Path.GetExtension(assetName) != "xnb")
{
assetPath = assetName + ".xnb";
}
else
{
assetPath = assetName;
}
return OpenRead(assetPath);
}
public Stream OpenRead(string fileName)
{
Stream stream = null;
string path;
try
{
if (Path.IsPathRooted(fileName))
{
path = fileName;
}
else
{
path = Path.Combine(RootDirectory, fileName);
}
stream = File.OpenRead(path);
// Read the asset into memory in one go. This results in a ~50% reduction
// in load times on Android due to slow Android asset streams.
var memStream = new MemoryStream();
stream.CopyTo(memStream);
memStream.Seek(0, SeekOrigin.Begin);
stream.Close();
stream = memStream;
}
catch (FileNotFoundException fileNotFound)
{
throw new ContentLoadException("The content file was not found.", fileNotFound);
}
catch (DirectoryNotFoundException directoryNotFound)
{
throw new ContentLoadException("The directory was not found.", directoryNotFound);
}
catch (Exception exception)
{
throw new ContentLoadException("Opening stream error.", exception);
}
return stream;
}
}
使い方は普通のContentManagerと大して変わらない。
// Androidであれば「/data/data/アプリID/files」あたりが返ってくるはず。
var path = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
var cm = new ExternalContentManager(this.Content.ServiceProvider, path);
var font = cm.Load<SpriteFont>("IPA-Gothic");