もんしょの巣穴blog

Serializableはうまくいく

最近はマルチは『Halo3』、オフラインは『ライデンファイターズエイシズ』のデモか『トリガーハートエグゼリカ』というゲーム生活。
『エグゼリカ』はもう少しでノーマルワンコインクリアできるんですが、まだまだ努力が足りてない。
4面の後半はだいぶうまくなってきてますが、やはり1ボムは確実に使用してしまいます。
それ以上に4面ボスが苦手。5面のフェインティアの方が楽だ。
『ライデン?』前にはワンコインクリアしたいところなんですがねぇ…。

昨日の追記を書いた後にSerializeを試してみました。
結果は成功。派生クラスではまだ試していませんが、大丈夫なんじゃないかと。
方法はやっぱりMemoryStreamを使う。
MemoryStreamに対してBinaryFormatterでSerializeして、MemoryStream.GetBuffer()メソッドでbyte[]を取得してこれをWriteObject()で出力。
読み込みの際はMemoryStreamのコンストラクタにReadObject()で取得できるbyte[]を渡して、これを利用してDeserializeすればOK。
もちろんこの方法はバイナリ出力側と入力側のオブジェクトが一致していないとアウトです。
ただ、これで弾幕ツールをXNA上で動かすこともできるかも。まあ、重そうだけど。
そのうち試してみようと思います。その前にもう少しXNAの勉強をしますが。

そういえば、シェーダ関係のサンプルはXNAでやっても問題ないんでしょうか?
やめて欲しい人がいるのであれば今までどおりC++でやりますが。

スポンサーサイト
  1. 2008/03/24(月) 20:03:45|
  2. プログラミング
  3. | トラックバック:0
  4. | コメント:1

ContentPipelineのWriteRawObject

久しぶりにブログを更新したと思ったら、去年から更新してなかったのね。
というわけで今年最初の更新。
最近のお勧めは『ライデンファイターズエイシズ』です。
某PSPのアクションゲームはかなりやばい移植ということで盛り上がっていますが、こっちはかなりすばらしい移植ですよ。
ここまで愛に溢れた移植は珍しい。『ケツイ』とかもこれくらい頑張って移植してほしいところ。

最近XNAをいじり始めて、結構面白いと思うようになってきました。
今後、シェーダ関係のサンプルプログラムはこいつで作ろうかと思ったり思わなかったり。
でまあ、自作のデータを読み込んでみたりする方法について調べて、これが結構面白いと思ったわけで。
XNAではContentPipelineというものがデータの変換から読み込みまでをサポートしています。
個人でゲーム製作をやっているとかなり適当になってしまうのがゲームコンテンツの管理です。
コンテンツは基本的に実行ファイル以外のデータの事を指します。グラフィックデータやSE、BGMにキャラクタのパラメータデータなんかもコンテンツです。
会社によってこれらの管理方法は色々違うでしょうけど、たいていはコンバータがあって、必要なデータをコンバータに通してバイナリ変換するパイプラインを誰かがメイクファイルとか使って書くのが多いのではないかと思います。
もちろん、メイクファイルを直接書かずに自動的にメイクファイルを作成するプログラムを作っていたりもするでしょう。
こいつをプロジェクトとして作ってしまっているのがContentPipelineで、コンバートから読み込みまでC#のプログラムで書いてしまうわけです。
詳しくは”ひにけにXNA”とか見るといいかと。詳しく丁寧に書いてあります。
で、あちらではカスタムパイプラインの作成時に WriteObject() メソッドを使用しているのですが、これは規定のクラスや構造体にしか使えません。
たとえば、カスタムパイプラインのためのカスタムクラスを作成したとします。で、このカスタムクラス内に別のカスタムクラスが使用されているとします。
これを WriteObject() で出力しようとするとエラーになります。そんなの知らねぇよ、って言われて。
C#でバイナリ出力する場合によく(?)使われる BinaryFormatter は[Serialize]属性さえ付いていれば内包しているクラスや構造体でも出力してくれるので楽なんですが、ContentPipelineではそうもいかないようです。
そこで使われるのが WriteRawObject() メソッドです。これはあるオブジェクトを対応したContentTypeWriterで出力するという代物です。
例えばこんな感じで構造体を内包するクラスがあるとします。

public struct MyStruct{
    public int data;
};

public class MyClass{
    public float Data;
    public MyStruct subData;
}

MyClass.Data は ContentWriter.Write() メソッドで出力できますが、MyClass.subData は簡単には出力できません。
まず、MyStruct を出力する ContentTypeWriter を作成します。

[ContentTypeWriter]
public class MyStructContentWriter : ContentTypeWriter<MyStruct>
{
    protected override void Write(ContentWriter output, MyStruct value)
    {
        output.Write( value.data );
    }
    public override string GetRuntimeType(Microsoft.Xna.Framework.TargetPlatform targetPlatform)
    {
        return typeof( MyStruct ).AssemblyQualifiedName;
    }
    public override string GetRuntimeReader(Microsoft.Xna.Framework.TargetPlatform targetPlatform)
    {
        return typeof( MyStructReader ).AssemblyQualifiedName;
    }
}

次にこいつを使って出力する処理を MyClass の ContentTypeWriter で行います。

[ContentTypeWriter]
public class MyClassContentWriter : ContentTypeWriter<MyClass>
{
    protected override void Write(ContentWriter output, MyClass value)
    {
        output.Write( value.Data );
        output.WriteRawObject<MyStruct>( value.subData, new MyStructContentWriter() );
    }
}

とこんな感じです。それほど難しくはありませんが、fwrite() とかと比べると面倒ですね。
とはいえ、Xbox360とPCではエンディアンの違いがあるので、ここで吸収されると思えば安いものかもしれませんが。
ContentTypeReader の方も同様に ReadRawObject を使用します。使い方はほぼ同じです。
一応これでカスタムクラス/構造体内部のカスタムクラス/構造体の出力方法はわかりましたが、内部のクラス/構造体が配列だったりしたらどうなるでしょう?
もっとスマートなやり方もあるのかもしれませんが、私はこうやりました。

int  num = value.subData.GetLength( 0 );
output.Write( num );
for( int i=0; i<num; i++ ){
    output.WriteRawObject<MyStruct>( value.subData[i], new MyStructContentWriter() );
}

まあ、綺麗とは言いがたいですね。でも、一応これで問題なかったことだけは確かです。
派生クラスが多い場合は派生クラスが対応する ContentTypeWriter を作成するようにするのがスマートかな。
派生クラスならどうなる?とかも試してみないとなんとも言えませんね。
今度試してみます。

[ContentPipelineのWriteRawObject]の続きを読む
  1. 2008/03/23(日) 18:51:56|
  2. プログラミング
  3. | トラックバック:0
  4. | コメント:0

プロフィール

monsho

Author:monsho
ゲームプログラマ?

最近の記事

最近のコメント

最近のトラックバック

月別アーカイブ

カテゴリー

ブロとも申請フォーム

この人とブロともになる

ブログ内検索

RSSフィード

リンク

このブログをリンクに追加する