「XNAメモ」カテゴリーのアーカイブ

久しぶりの更新です!

今回はXBOXでデバイスを選択し、セーブ、ロードをする手順を紹介して欲しいとのリクエストがあったので、それに関して紹介します。

ただ、現在自分がXBOXでテスト出来る環境が無い為、まずはWindowsでは動作確認出来た物を紹介します(と言っても両方で使用出来るはずなので、ただ単にテストしてないだけです)。

まずはテスト用に保存するクラスを作成します。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Xml.Serialization;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.GamerServices;

namespace Save
{
[XmlInclude(typeof(Unit)), XmlInclude(typeof(Player))]
public class GameObject
{
public Vector3 Position { get; set; }
}

public class Unit : GameObject
{
public float Life { get; set; }
}

public class Player : GameObject
{
public string Name { get; set; }
}

今回はセーブデータをxml形式で保存します。その際、作ったクラスを保存する為にGameObjectクラスの宣言部分にXmlIncludeを追加しています。

次に実際にセーブするデータを保存するクラスを宣言します。内容はテスト用に適当に詰めたので、必要に応じて変更してください。

public class SaveGameData
{
public string PlayerName;
public Vector2 AvatarPosition;
public int Level;
public int Score;
public List<GameObject> GameObjects;
}

下準備が出来たので、実際にセーブ関連の実装を始めます。

今回はセーブだけを紹介しますが、ロードを実装する時と途中まで処理が一緒なので(実際にデータを扱う所までは同じです)、元となるクラスを実装した後に、それを継承したセーブ用クラスを作成します。

XNA(XBOX)でセーブデータを扱う際、以下の手順が必要です:
– 保存先、もしくは読込先、のデバイスの選択(と獲得)
– 保存先、もしくは読込先、のコンテナの選択(と獲得)
– 選択先のデバイス+コンテナへのファイル操作

Windowsで実行する場合デバイスは勝手に選択され、セーブデータはマイドキュメント上に作成されます(自分の場合は「SavedGames」と言う名前のフォルダが作成され、保存されていました)。

なのでまず、これらのステップを表すenumを宣言します。ついでに、変数も記載しておきます。

class SaveDataHandler
{
protected enum States
{
None,                        // 何も無い状態
ReadyToSelectStorageDevice,  // デバイス選択を出来る状態
SelectingStorageDevice,      // デバイス選択中
ReadyToOpenStorageContainer, // コンテナをあける準備が出来た状態
OpeningStorageContainer,     // コンテナを開いている最中
HandlingFile                 // ファイル操作中
}

protected StorageDevice m_storageDevice;
protected States m_state;
protected IAsyncResult m_asyncResult;
protected StorageContainer m_storageContainer;
protected PlayerIndex m_controller;
protected string m_filename;    // 保存されるファイル名
protected string m_diplayName;  // 使用されるコンテナ(ディレクトリ)名
protected SaveGameData m_data;

public SaveDataHandler()
{
m_data = null;
m_state = States.None;
m_storageContainer = null;
m_storageDevice = null;
m_filename = "";
m_diplayName = "";
}

public SaveGameData Data
{
get { return m_data; }
set { m_data = value; }
}

次に更新処理です。更新処理内では、先ほど宣言したenumを使って現在の状態を記録し、ソレに応じて違う処理を行います。

まずは、デバイス選択を始める前の処理です。

public void Update(float delta)
{
switch (m_state)
{
case States.ReadyToSelectStorageDevice:
#if XBOX
if (!Guide.IsVisible)
#endif
{
// デバイス選択を表示(Windowだと表示されない)
m_asyncResult = StorageDevice.BeginShowSelector(m_controller, null, null);
m_state = States.SelectingStorageDevice;
}
break;

GuidはXBOX用の処理なので、Windows版では出ないように#if処理を付けています。

次にデバイスを選択する処理です。デバイス選択が終わったら、次に進む処理を実装しています。

case States.SelectingStorageDevice:
if (m_asyncResult.IsCompleted)
{
// デバイス選択を閉じる(Windowだと表示されない)
m_storageDevice = StorageDevice.EndShowSelector(m_asyncResult);
m_state = States.ReadyToOpenStorageContainer;
}
break;

次は、コンテナを開く準備です。

case States.ReadyToOpenStorageContainer:

if (m_storageDevice == null || !m_storageDevice.IsConnected)
{
// デバイスを獲得出来ていない場合、もしくは接続されていない場合は、選択に戻る
m_state = States.ReadyToSelectStorageDevice;
}
else
{
// 指定されたゲームのコンテナを開く
m_asyncResult = m_storageDevice.BeginOpenContainer(m_diplayName, null, null);
m_state = States.OpeningStorageContainer;
}
break;

次はデバイスと同じく、終了したら次に進む処理を追加します。

case States.OpeningStorageContainer:

if (m_asyncResult.IsCompleted)
{
// コンテナを開いたので、処理を終了させる
m_storageContainer = m_storageDevice.EndOpenContainer(m_asyncResult);
m_state = States.HandlingFile;
}
break;

そしてファイル操作時の処理を実装します。

case States.HandlingFile:

if (m_storageContainer == null)
{
// コンテナを獲得できていない場合、戻る
m_state = States.ReadyToOpenStorageContainer;
}
else
{
try
{
// 継承されたクラスで行われる、ファイル処理
Process(m_data);
}
catch (IOException e)
{
// 問題があった場合
OnError(e.Message);
}
finally
{
// コンテナを破棄し、他も初期化する
m_storageContainer.Dispose();
m_storageContainer = null;
m_state = States.None;
m_filename = "";
m_diplayName = "";
}
}
break;
}
}

protected virtual bool Process(SaveGameData data)
{
return false;
}

protected virtual void OnError(string exceptionMessage)
{
Debug.WriteLine(exceptionMessage);
}

ここで行われるProcess()とOnError()メソッドは、継承先のクラスで実際にセーブ処理や、ロード処理、そしてエラー処理を行います。

今回は面倒だから省きましたが、実装方法がしっくり来ない方は、abstractメソッドに変更する等すると、ましになるかもしれません。

更新処理は実装したので、次は処理を開始する(セーブ時に呼び出す)メソッドを実装します。やっている事は初期化だけです。

public void Start(string fileName, string displayName, SaveGameData data, PlayerIndex controller)
{
// 処理を開始 + 初期化
if (m_state == States.None)
{
m_data = data;
m_filename = fileName;
m_diplayName = displayName;
m_state = States.ReadyToOpenStorageContainer;
m_controller = controller;
}
}

このクラス内でなくても良いのですが、セーブ時用に既存のファイルを削除する処理を最後に追加します。

protected void DeleteExistingData(string fileName)
{
// 指定されたファイルが存在した場合、消去する
if (m_storageContainer.FileExists(fileName))
{
m_storageContainer.DeleteFile(fileName);
}
}

これでこのクラスの実装は終わりです。

では次にセーブ用クラスを実装します。行う事は、Process()メソッドをオーバーライドし、セーブ処理を行うだけです。データ保存には、XmlSerializerを使用します。

class DataSaver : SaveDataHandler
{
public DataSaver()
: base()
{
}

protected override bool Process(SaveGameData data)
{
// 存在するデータを消去
DeleteExistingData(m_filename);

// 保存処理
using (Stream stream = m_storageContainer.CreateFile(m_filename))
{
XmlSerializer serializer = new XmlSerializer(typeof(SaveGameData));
serializer.Serialize(stream, data);
}

return true;
}
}

これでセーブ用クラスの実装は全部終わりました。後は使用するだけです。

SaveGameData saveGameData = new SaveGameData()
{
PlayerName = "Ookumaneko",
AvatarPosition = new Vector2(2, -50),
Level = 10,
Score = 9999999990,
GameObjects = new List<GameObject>(),
};

dataSaver.Start("SaveData1.sav", "TetrangleSaveData", saveGameData, PlayerIndex.One);

以上です。これでマイドキュメント上にファイルが追加されるはずです。

今回は、PC、XBOX両方で出来るだけ同じコードを使用出来る用に、XNAと.Netの機能を主に使用しています。なので、PCに限定してゲームを作る際、データを暗号化していないので、紹介した処理をそのまま使用するのはあまりオススメしていません。

使用したい場合は、何かファイルをバイナリー化したり、暗号化するなど変化を加えてから使用してください。XBOX編はテスト出来たら紹介します(内容はほぼ変わらないと思いますが)。

今回は、内容が長くなった為、説明がざっくりしすぎている所が結構あるので、何時か補足を追加するかもしれません。

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

 

今回は、リクエスト内容が合っているのかは判断付きませんでしたが、合っていると思い込み、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

 

今回は、コントローラーが抜けた事を察知する処理をメモります。

 

何故コントローラーが抜けた事をプログラム的に実装する必要があるかと言うと、抜けた時にゲームをポーズしたり、何らかの処理を行いたい時の為です。

 

では実装に入ります。行う事は、前のフレームにコントローラーが繋がれていて、現在のフレームでは繋がれていないかの確認だけなのです。

 

まずは必要な変数を宣言します。

GamePadState[] m_PadState = new GamePadState[4];
GamePadState[] m_OldPadStates = new GamePadState[4];

 

次に、コントローラーの状態を毎フレーム更新します。

 

protected override void Update(GameTime gameTime)
for (int i = 0; i < 4; i++)
{
m_OldPadStates[i] = m_PadState[i];
m_PadState[i] = GamePad.GetState((PlayerIndex)i);
}
}

 

次に、コントローラーが切断されたかを確認するメソッドを実装します。

 

public bool WasDisconnected(PlayerIndex index)
{
return ( !m_PadState[(int)index].IsConnected && m_OldPadStates[(int)index].IsConnected );
}

 

後は、実装したメソッドを呼び出すだけです。

 

if (WasDisconnected(PlayerIndex.One))
{
// ゲームをポーズする・・・?
}

 

今回は以上です。

本来ならばこの処理はイベント型にした方がいい気がしますが、それはまたの機会と言うことにしておきます(その機会が来ない気もしますが・・・)。

 

久々の、XNAメモです! …と言っても短いですが。

今回は、シンプルな画面のフェードアウト、フェードイン処理の実装方法をメモります。

 

今回の処理は、恐らく一番楽な単純に画面全体を徐々に暗くしていく処理を実装します。
実際に行う事は、画面全体にテクスチャアを描画し、描画時にSpriteBatch.Draw()メソッドに渡す色のアルファ値を、毎フレーム変更するだけです。
ではまず、必要な変数の宣言と初期化です。

Texture2D m_pixel;
float m_alpha;
float m_alphaIncAmount;
bool m_isFadeOut;

public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";

m_alpha = 0.0f;
m_alphaIncAmount = 0.004f;
m_isFadeOut = true;
}

 

次は実際にフェードの処理を実装します。ここで描画で使用するアルファ値を変化します。

 

private void UpdateFade()
{
m_alpha += m_alphaIncAmount;
if (m_alpha >= 1.0f)
{
m_alpha = 1.0f;
m_isFadeOut = false;
}
}

 

後は描画するだけです。

 

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

// 画面のサイズ
Rectangle screenBound = new Rectangle(0, 0, 800, 600);

// フェードの色
Color colour = new Color(0.0f, 0.0f, 0.0f, m_alpha);

// 描画
spriteBatch.Begin();
spriteBatch.Draw(m_pixel, screenBound, colour);
spriteBatch.End();

base.Draw(gameTime);
}

 

結果は以下の用になります。

 

 

フェードインの処理は、アルファ値の上げる代わりに下げるだけで実装できます。UpdateFade()メソッド内の処理を以下の用に変更すると、フェードアウトと、フェードインが交互に行われます。

 

private void UpdateFade()
{
if (m_isFadeOut)
{
m_alpha += m_alphaIncAmount;
if (m_alpha >= 1.0f)
{
m_alpha = 1.0f;
m_isFadeOut = false;
}
}
else
{
m_alpha -= m_alphaIncAmount;
if (m_alpha <= 0.0f)
{
m_alpha = 0.0f;
m_isFadeOut = true;
}
}
}

 

結果、以下の用に変わります。

 

 

今回はココまでです。

何時も通りネタが無いので、XNAにしろUnityにしろ、何か取り上げて欲しい事がある方は、ご連絡かコメントください m_ _m

(だからと言ってリクエストに自分の技術が合うかはわかりませんが・・・)

 

このメモでは爆発や、強烈な攻撃などの衝撃の際に、画面が振動して見えるエフェクトの作り方を紹介します。

 

上記の説明だけではわかりにくいので、まずは以下の画像をご覧ください:

通常のゲーム画面

振動エフェクト中のゲーム画面

 

振動のエフェクトは上にある2番目の画像の用に始まり、徐々に薄くなって最終的には元に戻るようになります。

 

では実装に入ります。まず、エフェクトで振動の為に実際に使われる数値などを保存するクラスを作ります。

 

class Blast
{
public float Amount;    // 量
public float Megnitude; // 震度
public Vector2 Center;  // 中心点

 

今回実装するクラスは、簡易化のため、基本的にメンバ変数はpublicにしています。次に、更新処理です。ここでは、徐々に振動を減らすための計算に使用されるAmountメンバ変数を徐々に下げていきます。

 

public void Update(float delta)
{
if (Amount >= 0.0f)
{
Amount -= delta;
}
else if (Amount < 0.0f)
{
Amount = 0.0f;
}
}

 

これでBlastクラスの実装は終わりです。次に、Blastクラスを使用して実際にエフェクトを作製し、描画するクラスを宣言します。

 

static class ShakeManager
{
public static Blast Blast = new Blast();
public static int ShakeStrength = 4;     // エフェクトの濃さ

 

このクラスも、使用しやすいようにstaticクラスにしています。ですが、staticクラスでなくてはいけないということは無いので、staticを外して実装しても問題は無いです。

 

次は、更新処理を実装します。今回は、BlastクラスのUpdate()メソッドを呼ぶだけです。

 

public static void Update(float delta)
{
Blast.Update(delta);
}

 

次に、Blastクラスの値を設定するメソッドを実装します。このメソッドは衝撃のエフェクトを使用し始めたい時に呼びます。

 

public static void SetupBlast(float magnitude, Vector2 center)
{
Blast.Amount    = magnitude;
Blast.Megnitude = magnitude;
Blast.Center    = center;
}

 

今度は描画処理を実装します。振動のエフェクトもこのメソッド内で計算され、描画されます。

 

描画処理のメソッドには、引数の一つとして、ゲーム画面を保存しているテクスチャアを送る必要があります。エフェクトを作製する際、送られてきた画像を元に手を加える事で振動のエフェクトを実装しているので、描画の度に最新のゲーム画面を送る必要があります。

 

public static void Draw(SpriteBatch sp, Texture2D gameContent)
{
if (Blast.Amount > 0.0f)
{
// 設定されている震度の濃さの分だけエフェクトを描画する
for (int i = 0; i < ShakeStrength; i++)
{
// 原点を計算する
Vector2 origin = ShakeManager.Blast.Center / 2;

// 描画位置を計算する
Vector2 pos = Blast.Center - origin;

// アルファ値を計算する
float alpha = 0.35f * (Blast.Amount / Blast.Megnitude);
Color col = new Color(1.0f, 1.0f, 1.0f, alpha);

// 大きさを計算する
float scale = (1.0f + (Blast.Megnitude - Blast.Amount) *
               0.1f + ((float)(i + 1) / 40.0f));

// エフェクトを描画する
sp.Draw(gameContent, pos, null, col, 0.0f,
origin, scale, SpriteEffects.None, 1.0f);
}
}
}

このエフェクトの実装には、実際に弄ってみてよく見える数値を探し、そのまま使用している為、数値がそのまま入っている部分が多くなっています。

 

エフェクトの実装は終了したので、後は実際に使うだけです。まず、エフェクトの初期化をします。第2引数に入れた数値は、サンプルでの画面の中心点です。

 

ShakeManager.SetupBlast(1.5f, new Vector2(400, 300));

 

次に更新処理を呼びます。

 

public override void Update(GameTime gameTime)
{
float delta = (float)gameTime.ElapsedGameTime.TotalSeconds;
ShakeManager.Update(delta);
base.Update(gameTime);
}

 

そして描画時に実際に使用してみます。

 

</pre>
public override void Draw(GameTime gameTime)
{
GraphicsDevice.SetRenderTarget(0, m_renderTarget);
GraphicsDevice.Clear(Color.Black);

// ゲーム画面をテクスチャアに描画する
DrawContent(gameTime);

GraphicsDevice.SetRenderTarget(0, null);
spriteBatch.Begin();


// ゲーム画面を描画する
spriteBatch.Draw(m_renderTarget, Vector2.Zero, Color.White);

// エフェクトを描画する
ShakeManager.Draw(spriteBatch, m_renderTarget);
spriteBatch.End();
}

 

以上です。これで、最初に紹介した画像の様なエフェクトが描画されます。エフェクトの見かけは、振動の初期価値と、ShakeManager.ShakeStrengthの値を変更すると結構変わるので、試してみてください。