MonoGame/外部のXNBファイルを読み込む

提供: MonoBook
移動: 案内検索

MonoGameで作っているゲームに起動時データ更新を実装し、プログラム以外の画像や音楽などだけであれば自動更新するようにしたいわけだ。 むしろプログラムの細かい部分もNLuaに投げるようにしてしまいたい。

そうなると外部から各種データを読み込む必要が出てくる。 しかしMonoGameで扱う各種データの基本はプログラムに組み込まれた「Content」である。

Xamarin.Android[編集]

MonoGameのContentManagerクラスのソースコードをざっと見た感じ、Xamarin.Androidではアセット以外のファイルへはアクセスできないようだ。 ContentManagerクラスのソースコードをみると幸いにもvirtualやらがチラホラ見受けられ継承してoverrideする前提の構造になっている。 なので細かいことは考えずにoverrideしてしまえ。

ついでに各種パラメータを保存したcsvjsonなどのxnb以外のファイルも同様の手順で読み込めるように、xnbパーサを経由しない裏口も用意すると捗った。 個人的な主な用途としては、OpenGL ESバーテックスシェーダーは書き込みだけで読み出せないため、モデルとは別に頂点データのcsvファイルを読み込み、それを当たり判定に用いている。

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");

関連項目[編集]