「ゲームプログラミング」タグのついた投稿

近況報告: Psmで新作リジェクトされました! orz

 

 

・・・は置いといて、今回はxmlを読み込み、一つのクラスか構造体の配列を保存する時に使う処理を少し使い回す方法を紹介します。今回はXNAが関係無いので、C#メモ扱いにしています。

 

まずはxmlを読み込み、配列を保存するクラスの宣言をします。

 

 

using System;
using System.Collections.Generic;
using System.Xml.Serialization;

public class XmlTable<T> where T : class, new()
{
    // XMLファイルの拡張子
    public const string _FILE_EXTENSION = ".xml";

    protected T[] m_data;

    public XmlTable()
    {
        m_data = null;
    }

 

 

Generic(T)の部分に読み込むデータの型を指定し、変数としてその型の配列を保存しています。今回はxml前提で作成していますので、拡張子名も設定しています。

 

 

それでは次は読み込む処理です。やる事はファイルを読み込み、XmlSerializerを使用して上記で指定したデータ型の配列に変換するだけです。

 

 

    public virtual void Load(string filepath, string extention = _FILE_EXTENSION)
    {
        XmlSerializer serializer = new XmlSerializer(typeof(T[]));
        System.IO.FileStream fileStream = System.IO.File.OpenRead(filepath + extention);
        m_data = (serializer.Deserialize(fileStream) as T[]);
        fileStream.Close();
    }

 

 

読み込みが終わりましたので、読み込んだデータを獲得する処理を作ります。

 

 

    public T GetData(int index)
    {
        if (m_data == null)
        {
            return null;
        }

        int length = m_data.Length;
        if (index < 0 || index >= length)
        {
            return null;
        }

        return m_data[index];
    }

 

 

後は実際に使用するだけです。やる事はデータ型の宣言、XmlTableクラスのインスタンス化、Load()処理を呼び、データを獲得します。

 

 

    public class CharaData
    {
        public int ID = -1;
        public string Name = "";
        public float MoveSpeed = 0.0f;
        public int SheetSizeX = 1;
        public int SheetSizeY = 1;
        public float AnimationTime = 0.0f;
    }

XmlTable<CharaData> m_charaTable;

        public void Load()
        {
            // インスタンス生成
            m_charaTable = new XmlTable<CharaData>();

            // 読み込み
            m_charaTable.Load( GetTablePath() );

            // データ獲得
            CharaData data = m_charaTable.GetData(0);
        }

 

 

以上です。今回は素のままの使用法を紹介しましたが、自分で使う場合はそのまま使う時と、これを継承して個別のデータ獲得方法を作ったりして使用しています。

 

 

    public class CharaDataTable
        : XmlTable<CharaData>
    {
        public CharaDataTable()
            : base()
        {
        }

        public CharaData FindData(int charaID)
        {
            int size = m_data.Length;
            for (int i = 0; i < size; ++i)
            {
                if (m_data[i].ID== charaID)
                {
                    return m_data[i];
                }
            }

            return null;
        }

 

 

今回はここまでです。一応ソース置いておきます:

そーす

久しぶりの更新です!・・・もうちょっと頻繁に何か書くようがんばります。

 

今回は、リクエスト内容が合っているのかは判断付きませんでしたが、合っていると思い込み、XNA4で動画を再生する方法を紹介します。(違った場合はご連絡ください)

 

動画を再生するには、プロジェクトにMicrosoft.Xna.Framework.Video への参照を通す必要があります。

 

その為には、ソリューションエクスプローラー内の参照フォルダを右クリックし「参照を足す」をクリックしてください(自分は英語版を使用しているので、出てくる文字があっていない場合はすみません m_ _m)。

 

 

次に「.NET」のタブから、Microsoft.Xna.Framework.Videoを選択肢、プロジェクトに追加してください。

 

次に動画を追加します。これはテクスチャ等と同様コンテントパイプラインで追加できますので、テクスチャア等と同じ手順で追加してください。

 

ここで気をつけなければいけないのは、コンテントパイプラインは、.WMVフォーマット以外の動画はサポートしていません。さらに、WMV9以降の「Main」設定でなければいけませんので、必要な場合はWindows Live Movie Maker等を使用して変換してください。

 

ではココからはコードに移ります。まず、動画の読込先、動画のプレイヤー、描画用のテクスチャアを宣言します。

 

Video m_video; // 再生する動画
VideoPlayer m_videoPlayer; // 動画プレイヤー
Texture2D m_videoTexture; // 描画用テクスチャ

 

次に、動画の読み込みと、初期化をします。

 

protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
m_videoTexture = null;

// 動画の読み込み
m_video = Content.Load<Video>("test");

// プレイヤーのインスタンスを作成
m_videoPlayer = new VideoPlayer();
}

 

次に、Update() 内で動画がまだ再生されていない場合、動画を再生する処理を追加します。

 

// もし動画を再生していなかったら再生する
if (m_videoPlayer.State == MediaState.Stopped)
{
m_videoPlayer.Play(m_video);
}

 

次に、描画処理です。動画の描画には、現在のコマをテクスチャアとして獲得し、それをSpriteBatchで描画します。

 

まず、テクスチャアの獲得です。

 

// 動画が再生中なら
if (m_videoPlayer.State != MediaState.Stopped)
{
// 動画から最新の画像を獲得
m_videoTexture = m_videoPlayer.GetTexture();
}

 

次に、現在のコマを再生します。

 

if (m_videoTexture != null)
{
spriteBatch.Draw(m_videoTexture, screenBounds, Color.White);
}

 

これで、動画が再生されます(読み込みに少し時間が掛かる時があります)。

 

 

後は補足ですが、動画を一時停止、一時停止解除、停止をする場合は、以下のメソッドを呼びます。

 

一時停止;

m_videoPlayer.Pause();

 

一時停止解除;

m_videoPlayer.Resume();

 

停止;

m_videoPlayer.Stop();

 

これらの状態は、Update内で紹介したのと同じ様にm_videoPlayer.Stateで判断できます。

 

今回は以上です。

当分の間何も書いていなかったので気が付きませんでしたが、なんと2万HITを越えました!!! XD

まさかこのブログに人が集まるとは・・・(とは言っても凄いところの一日分ぐらいですが)。

まだまだネタが無いので、何かリクエストがあったらコメントか、メールをお送りください m_ _m

 

今回は、前2回で作ったパーティクルシステムに、円形の様なエフェクトを追加します。

今回は実装方法として、一つのメソッド(Emit()メソッド)で別のエフェクトを作製します。なので、まずエフェクトの種類を表すenumを宣言します。

enum ParticleType
{
Linear, // 同じ速度のエフェクト
Spread, // ランダムな速度で移動
}

次に、Emit()メソッドに、ParticleTypeを渡せるようにします。その後に、ParticleTypeによって違う速度のパーティクルを作るようにメソッドの内容を変更します。

public void Emit(ParticleType type, Texture2D tex, Vector2 pos, int minAngle, int maxAngle, float scale,
float shrinkRate, float duration, int amount, int maxSpeed, Color colour)
{
Particle p;
float angle = 0.0f;
float speed = 0.0f;

for (int i = 0; i < amount; i++)
{
// ランダムな角度を獲得する
angle = m_rand.Next(minAngle, maxAngle);

if (type == ParticleType.Linear)
{
// 一定の速度を設定する。
speed = maxSpeed;
}

else if (type == ParticleType.Spread)
{
// ランダムな速度を獲得する
speed = m_rand.Next(1, maxSpeed);
}

// 新しいパーティクルを作る
p = new Particle(tex, pos, speed, angle, scale, shrinkRate, duration, colour);

// パーティクルを足す
m_particles.Add(p);
}
}

後は、Emit()メソッドの呼び出しを変更するだけです。最初のエフェクトは、360度に向けて作成され、二つ目は150~230度に向けて作成されます。

protected override void Update(GameTime gameTime)
{
// スペースキーが押されたら
if (Input.IsPressed(Keys.Space))
{
// パーティクルを作る
emitter.Emit(ParticleType.Linear, particleTex, new Vector2(300, 300), 0, 360, 1.0f, 1.0f, 1.5f, 100, 200, Color.Gold);
}

if (Input.IsPressed(Keys.Z))
{
float scale = 0.5f;
float shrink = 0.5f;
int speed = 600;
int min = 150;
int max = 230;
Vector2 pos = new Vector2(350, 200);
emitter.Emit(ParticleType.Linear, particleTex, pos, min, max, scale, shrink, 1.0f, 40, speed, Color.Salmon);
emitter.Emit(ParticleType.Linear, particleTex, pos, min, max, scale, shrink, 2.5f, 40, speed, Color.Pink);
emitter.Emit(ParticleType.Linear, particleTex, pos, min, max, scale, shrink, 2.0f, 40, speed, Color.Crimson);
}

// パーティクルの更新
emitter.Update((float)gameTime.ElapsedGameTime.TotalSeconds);

base.Update(gameTime);
}

変更後はこんな感じになります。

以上です。気が向いたら別のエフェクトも追加してみますが、やるかどうかは今の所未定です。

今回は前回作ったパーティクルシステムを少しだけ変更して、作製するパーティクルの向きを限定します。

*注意: 前回の記事を書いた後に気が付きましたが、ParticleEmitterクラスのEmmit()メソッドは、本来Emit()という名前の筈なのに間違えてました。申し訳ありませんが、気になる方は修正してください。

では実装にもどります。まず、ParticleEmitterクラスのEmit()メソッドの引数に、角度の最低値と最大値を追加します。パーティクルの角度を獲得する時に、こに2つのパラメータを使用してランダムの角度を獲得するように変更します(実質パラメーターと一行を変更するだけです)


public void Emit(Texture2D tex, Vector2 pos, int minAngle, int maxAngle, float scale,
float shrinkRate, float duration, int amount, int maxSpeed, Color colour)
{
Particle p;

for (int i = 0; i < amount; i++)
{
// ランダムな角度を獲得する
int angle = m_rand.Next(minAngle, maxAngle);

// ランダムな速度を獲得する
float speed = m_rand.Next(1, maxSpeed);

// 新しいパーティクルを作る
p = new Particle(tex, pos, speed, angle, scale, shrinkRate, duration, colour);

// パーティクルを足す
m_particles.Add(p);
}
}

後は、Emit()を呼ぶときに少し変更するだけです。


// スペースキーが押されたら
if (Input.IsPressed(Keys.Space))
{
// パーティクルを作る
emitter.Emit(particleTex, new Vector2(300, 300), 30, 80, 0.5f, 1.0f, 1.5f, 100, 200, Color.Gold);
}

if (Input.IsPressed(Keys.Z))
{
float scale = 0.5f;
float shrink = 0.5f;
int speed = 600;
int min = 150;
int max = 230;
Vector2 pos = new Vector2(350, 200);
emitter.Emit(particleTex, pos, min, max, scale, shrink, 1.0f, 40, speed, Color.Salmon);
emitter.Emit(particleTex, pos, min, max, scale, shrink, 2.5f, 40, speed, Color.Pink);
emitter.Emit(particleTex, pos, min, max, scale, shrink, 2.0f, 40, speed, Color.Crimson);
}

変更後の動画:

以上です。何か今回は凄く短くなってしまいましたが、パーティクルシステム関係のメモはまだ最低1回続く予定です。

今回は小規模なパーティクルシステムの実装についてメモります。先に書いておきますが、今回(と多分次回)作るシステムは簡単に実装するのを目的として作るので、柔軟性、拡張性、そして速度はかなり微妙です。なので、もしこれを参考にする事があれば、自分が使い易いように変更する事をオススメします。

まず、パーティクルを管理するクラスを作ります。ランダムの値を取る必要がある場合が多いので、Randomも保存してあります。

class ParticleEmitter
{
protected List<Particle> m_particles;
protected Random m_rand;

public ParticleEmitter()
{
m_particles = new List<Particle>();
m_rand = new Random();
}

次に、1個のパーティクルを表すクラスを作ります。このクラスはParticleEmitterクラス内で宣言し、ParticleEmitterクラスと、継承したクラスだけが使う使用にしています。コンストラクタ内では、前に紹介した角度から向きを計算する方法を使用し、Vector2の向きを獲得しています。


protected class Particle
{
public Texture2D m_texture;    //描画するテクスチャア
public Vector2   m_position;   // 位置
public Vector2   m_direction;  // 進む向き
public Vector2   m_origin;     // テクスチャアの原点
public float     m_duration;   // 寿命
public float     m_scale;      // 現在の大きさ
public float     m_shrinkRate; // 収縮する速度
public float     m_speed;      // 移動速度
public bool      m_isActive;   // 生きているかどうかのフラグ
public Color     m_colour;     // 色

public Particle(Texture2D tex, Vector2 pos, float speed, float angle, float scale, float shrinkRate, float duration, Color colour)
{
m_texture = tex;
m_position = pos;
m_scale = scale;
m_shrinkRate = shrinkRate;
m_isActive = true;
m_duration = duration;
m_colour = colour;
m_speed = speed;
m_origin = new Vector2(tex.Width / 2, tex.Height / 2);

// 角度をラジアンに変更する
angle = MathHelper.ToRadians(angle);

// 角度から、向きを獲得する
Vector2 up = new Vector2(0, -1.0f);
Matrix rot = Matrix.CreateRotationZ(angle);
m_direction = Vector2.Transform(up, rot);
}

次に、パーティクルのUpdate()メソッド実装します。今回する事は、コンストラクタ内で獲得した向きに向かって移動、小さくする、そして設定された寿命を越すか、大きさが0以下になった場合、破棄するフラグを立てます。


public void Update(float delta)
{
// パーティクルを移動する
m_position += m_direction * m_speed * delta;

// パーティクルを小さくする
m_scale -= m_shrinkRate * delta;

// 寿命を減らす
m_duration -= delta;

// 大きさが小さすぎるか、寿命が尽きたら
if (m_scale <= 0.0f || m_duration <= 0.0f)
{
// パーティクルを破棄する
m_isActive = false;
m_position = new Vector2(-100, -100);
}
}

後は描画のメソッドだけです。これは単純に描画するだけです。


public void Draw(SpriteBatch sp)
{
sp.Draw(m_texture, m_position, null, m_colour, 0, m_origin, m_scale, SpriteEffects.None, 0);
}

Particleクラスはこれで出来ましたので、次にParticleEmitterクラスの続きを実装します。まず、保存しているパーティクルを全て破棄するメソッドを作ります。これは単純にリストからClear()メソッドを呼ぶだけです。


public void Clear()
{
m_particles.Clear();
}

全てのパーティクルの更新と、フラグが立っている場合破棄をするメソッドを作ります。


public void Update(float delta)
{
// 破棄されているパーティクル
List<Particle> toRemove = new List<Particle>();

for (int i = 0; i < m_particles.Count; i++)
{
// パーティクルの更新
m_particles[i].Update(delta);

// もし現在のパーティクルが破棄されていた場合
if (!m_particles[i].m_isActive)
{
// 破棄するリストに追加
toRemove.Add(m_particles[i]);
}
}

for (int i = 0; i < toRemove.Count; i++)
{
// パーティクルをリストから除外する
m_particles.Remove(toRemove[i]);
}
}

今回の実装方法は、更新するループ内では破棄するパーティクルを除外せず、後で除外する用のリストに追加し、全てのパーティクルが更新し終わってから破棄されたパーティクルを除外しています。この実装方法ではなく、最初のループ内でリストから除外しても問題は特に無いです。

次は、全てのパーティクルを描画するメソッドを作ります。これはシンプルです。


public void Draw(SpriteBatch sp)
{
for (int i = 0; i < m_particles.Count; i++)
{
m_particles[i].Draw(sp);
}
}

最後に、実際にパーティクルを追加するメソッドです。このメソッドはパラメータを弄る事で少しの変化を付けられるように、渡す値が結構多くなります(次回のメモとかではさらに増える予定です)。

渡している値は順番で、テクスチャア、位置、大きさ、収縮するのに使う値、寿命、生成する量、最高速度、色です。

今回は基本的に、ランダムな向きに、ランダムな速度で移動するパーティクルを作る使用にしています。


public void Emmit(Texture2D tex, Vector2 pos, float scale, float shrinkRate,
float duration, int amount, int maxSpeed, Color colour)
{
Particle p;

for (int i = 0; i < amount; i++)
{
// ランダムな角度を獲得する
int angle = m_rand.Next(0, 360);

// ランダムな速度を獲得する
float speed = m_rand.Next(1, maxSpeed);

// 新しいパーティクルを作る
p = new Particle(tex, pos, speed, angle, scale, shrinkRate, duration, colour);

// パーティクルを足す
m_particles.Add(p);
}
}

後は実際に使うだけです。

protected override void Update(GameTime gameTime)
{
float delta = (float)gameTime.ElapsedGameTime.TotalSeconds;

// スペースキーが押されたら
if (Input.IsPressed(Keys.Space))
{
// パーティクルを作る
emitter.Emmit(particleTex, new Vector2(300, 300), 0.5f, 1.0f, 1.5f, 100, 200, Color.Gold);
}

if (Input.IsPressed(Keys.Z))
{
float scale = 0.5f;
float shrink = 0.5f;
int speed = 600;
emitter.Emmit(particleTex, new Vector2(300, 300), scale, shrink, 2.0f, 40, speed, Color.Salmon);
emitter.Emmit(particleTex, new Vector2(300, 300), scale, shrink, 2.0f, 40, speed, Color.Pink);
emitter.Emmit(particleTex, new Vector2(300, 300), scale, shrink, 2.0f, 40, speed, Color.Crimson);
}

// パーティクルの更新
emitter.Update(delta);

base.Update(gameTime);
}

これで実装後はこんな感じになります(使う画像によって見栄えは変わりますが)

以上です。Emmit()メソッドの値を弄ると少し違う見かけも物も作れます。

後1,2回これに関してメモする予定ですので、これで実装終了という訳ではありません。