忍者ブログ

Memeplexes

プログラミング、3DCGとその他いろいろについて

[PR]

×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。


かんたんXNA その8 3Dカメラのアニメーション

このページは古いです
最新版はこちら

前回は2Dでしたが今回は
3Dカメラのアニメーションを行います。

これもやはりGame.Updateメソッド内で
カメラの座標と向きを変えることによって
アニメーションを行っています。
 

 



using System;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;


public class MyGame : Microsoft.Xna.Framework.Game
{
    GraphicsDeviceManager graphics;
    BasicEffect effect;

    VertexPositionColor[] vertices = new VertexPositionColor[3];
    float angle = 0;

    public MyGame()
    {
        graphics = new GraphicsDeviceManager(this);

        vertices[0] = new VertexPositionColor(new Vector3(1, 1, 0), Color.Navy);
        vertices[1] = new VertexPositionColor(new Vector3(0, 0, 0), Color.White);
        vertices[2] = new VertexPositionColor(new Vector3(-1, 1, 0), Color.Red);
    }

    protected override void LoadGraphicsContent(bool loadAllContent)
    {
        if (loadAllContent)
        {
            effect = new BasicEffect(graphics.GraphicsDevice, null);
            effect.VertexColorEnabled = true;
            effect.Projection = Matrix.CreatePerspectiveFieldOfView(
                MathHelper.ToRadians(45),
                Window.ClientBounds.Width / (float)Window.ClientBounds.Height,
                1,
                100
                );
        }
    }

    protected override void Update(GameTime gameTime)
    {
        angle += 0.01f;
    }

    protected override void Draw(GameTime gameTime)
    {
        graphics.GraphicsDevice.Clear(Color.CornflowerBlue);

        graphics.GraphicsDevice.VertexDeclaration = new VertexDeclaration(
            graphics.GraphicsDevice,
            VertexPositionColor.VertexElements
            );

        effect.View = Matrix.CreateLookAt(
            new Vector3(3 * (float)Math.Sin(angle), 0, 3 * (float)Math.Cos(angle)),
            new Vector3(0, 0, 0),
            new Vector3(0, 1, 0)
            );

        effect.Begin();

        foreach (EffectPass pass in effect.CurrentTechnique.Passes)
        {
            pass.Begin();

            graphics.GraphicsDevice.DrawUserPrimitives<VertexPositionColor>(
                PrimitiveType.TriangleList,
                vertices,
                0,  //vertexOffset
                1   //primitiveCount
                );

            pass.End();
        }

        effect.End();
    }
}


このサンプルは三角形の周りをカメラがぐるぐる回ることによってアニメーションを行っています。
(三角形が回っているように見えますが、実際に回っているのはカメラの方です。
つまり天動説ではなく地動説というわけです。)


xnaSimplestTriangleWithCameraRotating1.JPGxnaSimplestTriangleWithCameraRotating2.JPGxnaSimplestTriangleWithCameraRotating3.JPG

ただし、これ以上回転して、裏から見ると三角形は見えなくなります。
xnaSimplestTriangleWithCameraRotating4.JPG
これはXNAが、
座標が時計回りの順番になっている三角形しか描画しない
からです。
xnaCullingDescription.JPG
回転して裏から見ると反時計回りの順番になっているように見え、
描画されなくなるのです。
(もちろん、さらに回転して元の時計回りの順番に見えるようになったら
再び描画されるようになります。)
これはパフォーマンスの問題で、
これによって描画が高速化するようです。


どうしても裏から見たときも描画したいという場合は、
GraphicsDevice.RenderState.CullModeCullMode.Noneに設定すると
裏側も描画されるようになります。

        graphics.GraphicsDevice.RenderState.CullMode = CullMode.None;

 xnaSimplestTriangleWithCameraRotating5.JPG
(ちなみに、RenderState.CullModeのデフォルトは
CullMode.CullCounterClockwiseFaceで、反時計回りは描画しないという意味です。
CullModeは全部で3つあり、
1つがデフォルトのCullCounterClockwiseFace、
2つ目がここで使ったNone(「描画しないということは無い」という意味)、
3つ目はCullClockwiseFaceで、デフォルトの逆です。)

拍手[0回]

PR

かんたんXNA その7 アニメーション

このページは古いです
最新版はこちら

ゲームで弾やキャラクターを動かすことを考えましょう。

一番簡単な方法は、一秒間に60回呼ばれているGame.Drawメソッドの中で
座標データを少しずつ変えて表示することです。

この方法は上手くいきます。

Drawメソッドの中で0.1動かせば一秒で6動くことになるでしょう。

しかし実際にはこの方法は使われなくて、
かわりに(やはり一秒に60回呼ばれる)Game.Updateメソッドの中でデータの変更を行います。

(※この理由はおそらくGUIアプリケーションで
ビューとモデルの分離を行う理由と同じでしょう。
データと、それの表示は分けておきたいものです。

「コンピュータサイエンスのいかなる問題も
間接層を付け加えることで解決できる」というわけです。
実際、UpdateメソッドはDrawメソッドとは
違う周期で呼ばれるように設定することも可能です。
Drawメソッドで何でもかんでもやっていたらそういうことは出来なかったでしょう。

まぁこういう小さいサンプルプログラムでは
デメリットの方が目立つような気もしますが。)


コードはこうなります。

 


using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
 
public class MyGame : Microsoft.Xna.Framework.Game
{
    GraphicsDeviceManager graphics;
    Microsoft.Xna.Framework.Content.ContentManager content;
    Texture2D texture;
    SpriteBatch spriteBatch;
    Vector2 position = new Vector2();

    public MyGame()
    {
        graphics = new GraphicsDeviceManager(this);
        content = new Microsoft.Xna.Framework.Content.ContentManager(Services);
    }

    protected override void LoadGraphicsContent(bool loadAllContent)
    {
        if (loadAllContent)
        {
            texture = content.Load<Texture2D>("FlyingSpaghettiMonster");
            spriteBatch = new SpriteBatch(graphics.GraphicsDevice);
        }
    }

    protected override void UnloadGraphicsContent(bool unloadAllContent)
    {
        if (unloadAllContent)
        {
            content.Unload();
        }
    }


    protected override void Update(GameTime gameTime)
    {
        position.X += 0.5f;
    }


    protected override void Draw(GameTime gameTime)
    {
        graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
        spriteBatch.Begin();
        spriteBatch.Draw(texture, position, Color.White);
        spriteBatch.End();
    }
}

このプログラムでは、空飛ぶスパゲッティーモンスターを少しずつ右に移動させています。

xnaSimplestAnimation1.JPGxnaSimplestAnimation2.JPGxnaSimplestAnimation3.JPG

 

拍手[0回]


かんたんXNA その6 3D三角形の表示

このページは古いです
最新版はこちら

ここでは3Dの三角形を表示します。
ここから、物を立体的に表示できるようになります。

物を立体的にウィンドウに表示するためには、
3次元の座標データだけでは足りません。
物はどこからどのように眺めるかによって
見え方が変わる
からです。

そのため、視点、見ている位置、視野などの情報が必要になります。
それらの情報から、3Dの座標データを2Dのウィンドウに表示できるように変換してやります。
この変換に使うのが、行列(マトリックス)です。

マトリックスというのは「データの変換の仕方を表すデータ」のことで、
物理学では例えばアインシュタインの特殊相対性理論の
「光速に近い速度を持った物体は縮み、時間はゆっくり進む」(ローレンツ変換)
というのを数式で表すのに使ったりします。
つまり、日常の、歪んでいないデータをマトリックスにかけて、
縮んだ、時間のゆっくり進むデータに変換しているということです。

XNAをやるのに特殊相対性理論など全く知らなくてかまいませんが、
どちらともデータの変換を行います。
XNAでは物体を縮ませる代わりに、3Dデータを
ウィンドウの2Dデータに変換するのにマトリックスを使います。
(他にも3Dモデルを回転させたり、平行移動させたり、拡大したりするのにも使います)

コードは次のようになります。


   
using Microsoft.Xna.Framework;   
using Microsoft.Xna.Framework.Graphics;
 
   
public class MyGame : Microsoft.Xna.Framework.Game   
{   
    GraphicsDeviceManager graphics;   
    BasicEffect effect;   
   
    VertexPositionColor[] vertices = new VertexPositionColor[3];
    public MyGame()   
    {   
        graphics = new GraphicsDeviceManager(this);
        vertices[0] = new VertexPositionColor(new Vector3(1, 1, 0), Color.Navy);   
        vertices[1] = new VertexPositionColor(new Vector3(0, 0, 0), Color.White);   
        vertices[2] = new VertexPositionColor(new Vector3(-1, 1, 0), Color.Red);   
    }
    protected override void LoadGraphicsContent(bool loadAllContent)   
    {   
        if (loadAllContent)   
        {   
            effect = new BasicEffect(graphics.GraphicsDevice, null);   
            effect.VertexColorEnabled = true;

            effect.View = Matrix.CreateLookAt(   
                new Vector3(0, 0, 3),   //カメラの位置
                new Vector3(0, 0, 0),   //カメラのターゲット
                new Vector3(0, 1, 0)    //カメラの上向きベクトル。(0, -1, 0)にすると画面が上下逆になる   
                );   
            effect.Projection = Matrix.CreatePerspectiveFieldOfView(   
                MathHelper.ToRadians(45),   //視野の角度。ここでは45度
                Window.ClientBounds.Width / (float)Window.ClientBounds.Height,//画面のアスペクト比(横 / 縦)  
                1,  //nearPlaneDistance:カメラからこれより近い物体は画面に映らない   
                100 //farPlaneDistance:カメラからこれより遠い物体は画面に映らない   
                );

        }   
    }
    protected override void Draw(GameTime gameTime)   
    {   
        graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
        graphics.GraphicsDevice.VertexDeclaration = new VertexDeclaration(   
            graphics.GraphicsDevice,   
            VertexPositionColor.VertexElements   
            );
        effect.Begin();
        foreach (EffectPass pass in effect.CurrentTechnique.Passes)   
        {   
            pass.Begin();
            graphics.GraphicsDevice.DrawUserPrimitives<VertexPositionColor>(   
                PrimitiveType.TriangleList,   
                vertices,   
                0,  //vertexOffset   
                1   //primitiveCount   
                );
            pass.End();   
        }
        effect.End();   
    }   
}



xnaSimplestTriangle3D.JPG

ここでやっているのは、BasicEffect.ViewプロパティとBasicEffect.Projectionプロパティの設定です。
このそれぞれに、3Dデータから2Dスクリーンデータへの変換の仕方を指定するのです。
何で2つのプロパティを設定しているのかというと、3Dから2Dへの変換は複雑なので
2回に変換を分けているというだけのことです(ですから原理上は1回にすることも出来ます)

public Matrix View { get; set; }

絶対的な座標データを視点(カメラ)に対する
相対的な座標系に変換するマトリックスを設定または取得します。

public Matrix Projection { get; set; }

「Viewプロパティのマトリックスで変換した3D座標データを
さらに2D画面に映す(プロジェクション)変換」
を表すマトリックスを設定または取得します。

ここにセットするマトリックスによって、
遠近法の効果がある映し方にしたり、
あるいはどんなに遠くになっても物が小さくならない映し方(図面を描くときにいいでしょう)
にすることにすることも出来ます。
ゲームを作るなら遠近法があったほうがいいですね。


さて、この変換を表すマトリックスを作るのには、別に難しい知識は必要なくて、
ただ単にMatrixクラスのクリエーション・メソッドを呼び出すだけでかまいません。
ここではMatrix.CreateLookAtメソッドと
Matrix.CreatePerspectiveFieldOfView(遠近法になります)メソッドを使って生成しています。
この2つに視点の位置や視野の広さなどを引数にしてマトリックスをつくり、
3Dデータを2Dに変換します。(実際に変換しているのはBasicEffectの内部ですが)

詳しく見ていきましょう。

public static Matrix CreateLookAt (
         Vector3 cameraPosition,
         Vector3 cameraTarget,
         Vector3 cameraUpVector
)


CreateLookAtメソッドは、最初の座標データをカメラに対する相対的な座標系に変換するマトリックスを作り出します。

cameraPosition視点(カメラ)の位置です。これを変えると物体を違う方向から眺めることが出来ます。
xnaSimplestTriangle3DWithCameraMoved.JPG※cameraPositionが(2, 0, 3)のとき

cameraTargetカメラのターゲットの位置です。ここを見ます。

cameraUpVectorカメラの上方向のベクトルです。
普通は(0, 1, 0)です(y軸方向が上)が、
これを逆さま(0, -1, 0)にすると、画面も逆さまになります。
xnaSimplestTriangleWithCameraUpDirectionInverted.JPG
もちろんこれを横(1, 0, 0)にすると、画面も横に傾きます。
xnaSimplestTriangleWithCameraUpDirectionX.JPG



次はMatrix.CreatePerspectiveFieldOfViewメソッドです。

public static Matrix CreatePerspectiveFieldOfView (
         float fieldOfView,
         float aspectRatio,
         float nearPlaneDistance,
         float farPlaneDistance
)

(MSDNより)

fieldOfView視野の角度です。人間が両目で見えるのは140°位
(実際はもっと広いのですが、目の端ではたいしたことはわかりません)
と言われていますが、そんなに広くしない方がいいでしょう。
広くすると迫力が出る一方、画面が歪んで3D酔いしやすくなるという意見があるからです。
このサンプルでは45°にしています。

なお、これは360が一回転の「度(°)」ではなくて
(6.28...)が一回転の「ラジアン」という単位を使います。
「ラジアンなんて使いたくない!」という方は
度→ラジアンへの変換を行うMathHelper.ToRadiansメソッドがありますので御安心を。

aspectRatioは表示する画面の四角形の形を表します。
具体的には
画面の横幅 / 縦幅
です。

nearPlaneDistancefarPlaneDistanceは一緒に解説した方がいいでしょう。
これらは、3Dモデルが表示されるカメラからの距離の範囲を表します。
逆に言うと、この2つにはさまれた領域の外では、物体は表示されません。
カメラに近すぎると表示されませんし、遠すぎても表示されなくなります。
この2つの引数は、その距離を表します。
このサンプルでは1と100に設定しています。
つまり、カメラとの距離が1以下のものは表示されず
100以上のものも表示されないということです。

 

拍手[0回]


かんたんXNA その5 三角形の表示

このページは古いです
最新版はこちら

ここまでは2Dでしたが、すこし3Dに近づきたいと思います
(といってもこの回はまだ2Dと変わりありませんが)

XNAの3Dでは(というかXNAに限ったことではないのですが)
物体を三角形の集合で表します。
ポリゴンですね。

たとえば、四角いドアを描こうと思えば、
2つの三角形を使います。
(それだけだと厚さのない薄っぺらいドアですが、
厚みを出そうと思えばその分6つの四角を描けばいいだけです)

丸いボールを描こうと思えば、ものすごく小さい三角形を
たくさん組み合わせて表します。

三角形の描画はCGの基本です。
というわけでまずは三角形を1つだけ表示してみましょう。


using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;


public class MyGame : Microsoft.Xna.Framework.Game
{
    GraphicsDeviceManager graphics;
    BasicEffect effect;
    VertexPositionColor[] vertices = new VertexPositionColor[3];

    public MyGame()
    {
        graphics = new GraphicsDeviceManager(this);
        vertices[0] = new VertexPositionColor(new Vector3(1, 1, 0), Color.Navy);
        vertices[1] = new VertexPositionColor(new Vector3(0, 0, 0), Color.White);
        vertices[2] = new VertexPositionColor(new Vector3(-1, 1, 0), Color.Red);
    }

    protected override void LoadGraphicsContent(bool loadAllContent)
    {
        if (loadAllContent)
        {
            effect = new BasicEffect(graphics.GraphicsDevice, null);
            effect.VertexColorEnabled = true;
        }
    }

    protected override void Draw(GameTime gameTime)
    {
        graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
        graphics.GraphicsDevice.VertexDeclaration = new VertexDeclaration(
            graphics.GraphicsDevice,
            VertexPositionColor.VertexElements
            );
        effect.Begin();

        foreach (EffectPass pass in effect.CurrentTechnique.Passes)
        {
            pass.Begin();
            graphics.GraphicsDevice.DrawUserPrimitives<VertexPositionColor>(
                PrimitiveType.TriangleList,
                vertices,
                0,  //vertexOffset
                1   //primitiveCount
                );
            pass.End();
        }

        effect.End();
    }
}



xnaSimplestTriangle.JPG


このサンプルは三角形を1つだけ表示しています。
その座標は、
(1, 1)
(0, 0)
(-1, 0)
です。
(このとおり、座標はウィンドウの左上が(-1, 1)、右上が(1, 1)、
左下が(-1, -1)、右下が(1, -1)となっています。)


このサンプルでは色の付いた三角形ですが、
テクスチャの写った三角形を作ることも可能です。
(むしろ、実際にゲームを作るとなればそっちの方がメインになるでしょう。)
こういったことは頂点のデータで決めます。

ここで用いた頂点はVertexPositionColor構造体で、座標と色の2つのデータを持っています。
別の構造体を使えば、別の性質を持った三角形を描けるというわけです。
(例えば、VertexPositionTexture構造体を使えばテクスチャの写った三角形を作ることが出来ます。)

public struct VertexPositionColor

コンストラクタ:

public VertexPositionColor ( Vector3 position, Color color )

フィールド:

public Color Color
public Vector3 Position



さて、ここで
何で3次元の座標データを使ったのにやってることは2Dと変わらないんだ
と思うかもしれません。
その答えは、3次元のデータを扱えるだけの十分な情報を設定していないからです。
今回は詳しい説明はしませんが、3次元データを表示するには少なくとも視点が必要です。
その視点(とその他いろいろ)のデータを使って、
3次元データを2次元のディスプレイに表示できるデータに変換し、
ウィンドウに映し出す必要があるのです。
変換の仕方を設定しなければ立体的に表示できないのです。

ここで移っている3色の3角形は、データの変換の仕方が設定されていないため、
2次元のディスプレイに既に表示できるデータであると解釈されて、
そのまま表示されてしまったのです。

BasicEffect

ソースコードの詳しい部分を見ていきましょう。
目に付くのは、BasicEffectクラスです。
日本語に訳すと「基本的な効果」・・・で
何を言っているのかさっぱりわかりませんが、
これはつまり、データがどのように描画されるかを制御するクラスです。
これはかなり重要なクラスで、3Dで描画するときには常にこれを使うと考えていいでしょう。
(実際には自分でBasicEffectの代わりになるEffectを作ってもいいのですが、
これはとても大変で、下手をするとDirectXよりもはるかに難しくなります
どのくらい難しいかと言うと、HLSLという描画の方法を細かく指定するための言語を覚えて、
それでいちいち「どのピクセルがどんな色になるのか」といったことを指定しなければなりません。
これはサンプルには荷が重過ぎます。
BasicEffectはそのHLSLでのプログラミングの代わりをしてくれるのですが、
問題もあって、現時点ではDirectXでの描画よりも機能が少ないです。
これではDirectXを使わずにXNAを使う意義がわからなくなってきますが、
まぁ将来は何とかなるでしょう(多分))


public class BasicEffect : Effect

例えば、
先ほどの3Dデータから2Dデータへの変換の仕方、
表示するときにライトは使うのか、使うとしたらどのようなライトを使うのか・・・
表示するときに霧の効果(奥に行けば行くほどぼやける)を使うのか、使うとしたらどんな霧を使うのか・・・
表示するポリゴンはテクスチャを使うのか、使うとしたらどんなテクスチャを使うのか・・・
・・・といったものを設定します。

おそらく「どのように描画されるか」をEffect(効果)と表現したのでしょう。
でも究極的にはプログラムはどんなものだって効果と言えるわけですから、
無意味なネーミングであるような気もします。

話を戻すと、このBasicEffectのインスタンスを作った後
(ちなみにコンストラクタの2番目の引数は
リソースを共有するグループを表すEffectPoolのインスタンスを指定しますが
、ここでは別に共有とか何とかをするわけではないのでnullです)

さっそくBasicEffect.VertexColorEnabledプロパティで
「表示するときに色を考慮に入れるか」を設定しています。

public bool  VertexColorEnabled { get; set; }

なお、これを設定しないと、次のように色が考慮されなくなってしまいます:
BasicEffectにはいろいろな描画するモードがあって、
そのモードをオンにしてやらないと上手く表示されないと言うわけです。
xnaSimplestTriangleWithoutColor.JPG(注をわざわざ入れる必要はないとは思いますが、全部真っ白です)




Drawメソッドの中を見てみましょう。
ここではまず、GraphicsDevice.VertexDeclarationプロパティを設定しています。

このプロパティには「どのような頂点データを用いるか」を設定します
(これはBasicEffectではないんですね)
なお、ここには自分で作ったオリジナルの頂点構造体のデータを設定することも可能です
(後々そういうことは多くなってくるはずです)

public VertexDeclaration VertexDeclaration { get; set; }

このプロパティにセットするのは、頂点データの宣言を表す
Microsoft.Xna.Framework.Graphics.VertexDeclarationクラスです。

public class VertexDeclaration : IDisposable

コンストラクタ:

public VertexDeclaration (
    GraphicsDevice graphicsDevice,
    VertexElement[] elements
)


このelementsというのは頂点のそれぞれのフィールドの宣言を表す
VertexElementの配列です。
あらかじめ用意されている頂点の構造体は全てVertexElementsフィールドを持っているため、
単にそれを入れてやるだけでOkです。

その後、Effect.Beginメソッドを呼んでいます。最後にはEffect.Endメソッドです。
これはちょうどSpriteBatchクラスでBegin、Endメソッドを呼んでいたようなものです。
描画の前と後に呼んでやらなければなりません。

public void Begin()
public void Begin(SaveStateMode saveStateMode)


public void End()

さらに、Effect.CurrentTechniqueプロパティで現在の描画モードを取得し、
EffectTechnique.Passesプロパティで全てのエフェクトパスを取得しています。

public EffectTechnique CurrentTechnique { get; set; }

public EffectPassCollection Passes { get; }

エフェクトパス(Microsoft.Xna.Framework.Graphics.EffectPass)
というのはちょうどお絵かきソフトで言うレイヤーのようなもので、
いろんな描画の効果を別々に表したものです。
例えば、
描画したいものの大体の色を表すエフェクトパス、
物のハイライトを表すエフェクトパス、
周りの環境からの光の反射による色を表すエフェクトパス、
影を表すエフェクトパス、
その他いろいろです。
Effectを自分で作るときにはこれはそこそこ重要になるかもしれません。
もっともこのプログラムでは、拍子抜けなことに、エフェクトパスは1つだけですが。
(ペイントでお絵かきをすることを連想させます)

そしてそれぞれのエフェクトパスについて、
お決まりのEffectPass.Begin、EffectPass.Endメソッドを
描画の前と後に呼んでいます
(お絵かきソフトでそれぞれのレイヤーを表示しているようなものです)

public void Begin()
public void End()


この2つの間で呼ばれているのが
今回最も重要なGraphicsDevice.DrawUserPrimitivesジェネリクスメソッドです。

今回はこのメソッドで3角形を実際に描画しています。

public void DrawUserPrimitives<T> (
         PrimitiveType primitiveType,
         T[] vertexData,
         int vertexOffset,
         int primitiveCount
) where T : ValueType


primitiveTypeは頂点データの役割を表します。
どういうことかというと、実はこのメソッドが描画できるのは三角形だけではなくて
点でも線でもいいのです。
ここにはどの図形を描画するかを指定するのです。

public enum PrimitiveType

メンバ
LineList lineList.JPG
LineStrip  lineStrips.JPG
PointList pointlist.JPG
TriangleFan  triangleFans.JPG
TriangleList  triangleList.JPG
TriangleStrip  triangleStrips.JPG


vertexDataは頂点データです。
これは三角形の3つの頂点だったり、点だったり、
あるいは線の始点と終点の集合だったりします。

vertexOffsetはオフセット、つまり引数の頂点データの使い始める場所です。
これはまず普通0でしょう。

primitiveCountは描画する三角形の数です。
vertexData.Lengthを3で割った値を入れるといいでしょう。
といっても実際は三角形以外も描画できるわけですから、
点の数だったり(vertexData.Length)、
直線の数だったり(vertexData.Length / 2)
することもあります。



※なお、注意しておきたいことですが
この類のメソッド
(DrawUserPrimitives, DrawPrimitives, DrawUserIndexedPrimitives, DrawIndexedPrimitives)
はDrawメソッドの中で何回呼んでもかまいません!
ぼくが3Dを勉強し始めたときには、
この類のメソッド(特にDrawPrimitivesとDrawIndexedPrimitives!)を一回しか呼んではいけないと勘違いしていて、
ばかげたことに、物を1つしか表示できない時期が続きました。

 

拍手[0回]


かんたんXNA その4 画像ファイルを表示

このページは古いです
最新版はこちら

ここでは画像ファイルをウィンドウに表示します。

外部のファイルをあつかうには、ContentManagerを使います。
これは、画像ファイルや3Dモデル、フォントなどをゲームにロードするときに使います。
なお、このクラスを使うときには、XNA Game Studioを使わないと死にます
というのも、それらのファイル一つ一つに対応するxnbファイルを作らなければならないからです。
(しかもこいつはバイナリファイルです)

例えば、image.jpgというファイルを直接使うことはできません。
"image.xnb"というファイルを"image.jpg"から作って、それを使わなければならないのです。

XNA Game Studioはそれを自動で行ってくれます。
このことを全く気にする必要はありません。
ただ"image.jpg"ファイルをソリューションエクスプローラーにペーストするだけでいいのです。

以下がXNA Game Studioでビルドしたコードです。
 


using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;


public class MyGame : Microsoft.Xna.Framework.Game
{
    GraphicsDeviceManager graphics;
    Microsoft.Xna.Framework.Content.ContentManager content;
    SpriteBatch spriteBatch;
    Texture2D texture;

    public MyGame()
    {
        graphics = new GraphicsDeviceManager(this);
        content = new Microsoft.Xna.Framework.Content.ContentManager(Services);
    }

    protected override void LoadGraphicsContent(bool loadAllContent)
    {
        if (loadAllContent)
        {
            spriteBatch = new SpriteBatch(graphics.GraphicsDevice);
            texture = content.Load<Texture2D>("FlyingSpaghettiMonster");
        }
    }

    protected override void UnloadGraphicsContent(bool unloadAllContent)
    {
        if (unloadAllContent)
        {
            content.Unload();
        }
    }

    protected override void Draw(GameTime gameTime)
    {
        graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
        spriteBatch.Begin();
        spriteBatch.Draw(texture, new Rectangle(0, 0, 200, 150), Color.White);
        spriteBatch.End();
    }
}



xnaSimplestTextureLoadedByContentManager.JPG

このプログラムは、
"FlyingSpaghettiMonster.jpg"(から作られた"FlyingSpaghettiMonster.xnb")
をゲームにロードして表示しています。

まずContentManagerのインスタンスを作ります。
このときコンストラクタの引数としてGameのServicesプロパティを入れてやります。
(引数の型はSystem.IServiceProviderです)

どうしてこういうことをするのかというとGraphicsDeviceが欲しいからです(多分)
しかし、疎結合にするため、かわりに間接層、
System.IServiceProviderをコンストラクタの引数に取っているのでしょう(多分)。
そしてここから、IGraphicsDeviceServiceを手に入れ、
さらにそこからGraphicsDeviceを手に入れます。

(※Game.Servicesのかわりに、以下のサービスプロバイダのインスタンスを引数に入れることで
ContentManagerがどんなサービスを要求しているのかがわかります。

class MyServices : System.IServiceProvider
{
    Game game;

    public MyServices(Game game)
    {
        this.game = game;
    }

    public object GetService(System.Type serviceType)
    {
        game.Window.Title += " " + serviceType.ToString();
        return game.Services.GetService(serviceType);
    }
}
これによるとContentManagerは
Microsoft.Xna.Framework.Graphics.IGraphicsDeviceService
を要求していました。
このインターフェースを使うと、GraphicsDeviceの生成、ロスト、リセットなどのイベントや
GraphicsDeviceのインスタンスを手に入れることが出来ます。)


次にLoadジェネリックメソッドを使って"FlyingSpaghettiMonster.jpg"をロードしています。
(正確にはそれから作られたxnbファイルですが)

ここでロードしたリソースは、Game.UnloadGraphicsContent
メソッド内でContentManager.Unloadメソッドを使って解放しなければなりません。

 

拍手[1回]