2010年11月07日

【プログラム】XNAでゲームを作る【シーン管理3】

では、今回はSceneManagerを作っていきましょう。

public class SceneManager:DrawableGameComponent
{
}

DrawableGameComponentはMainのソースにコンポーネント登録するのに必要な宣言です。

これは後ほど

メンバ変数には以下を定義しておきます。

private List<SceneBase> scene = new List<SceneBase>();
private List<SceneBase> sceneToUpdate = new List<SceneBase>();
private SpriteBatch spriteBatch;
private SpriteFont spriteFont_small; // 必要な場合
private SpriteFont spriteFont_big; // 必要な場合

// コントローラ
private KeyboardPropaerty m_KeyInput;

bool isInitialize;
bool traceEnable = true;


モンスターハンターポータブル 3rd

新品価格
¥9,980から
(2010/11/6 22:51時点)


 

 
Gameコンポーネントを持っているのでGameクラス持っているため、コンストラクタは以下のとおり

public SceneManager(Game game)
 : base(game)
{
 // 何もしない
}

初期化関数を実装

public override void Initialize()
{
 base.Initialize();

 // 初期化完了
 isInitialize = true;
}

コンテントロード
protected override void LoadContent()
{
 ContentManager content = Game.Content;
 spriteBatch = new SpriteBatch(GraphicsDevice);
 spriteFont_small = content.Load<SpriteFont>("font/message14");
 spriteFont_big= content.Load<SpriteFont>("font/message20");
 
 m_KeyInput = new KeyboardPropaerty();

 // 各画面がコンテントを読み込む
 foreach (SceneBase sceneData in this.scene)
 {
  sceneData.LoadContent();
 }
}

Mainソースで明記するはずの部分をここに書いていきます。

// コンテントデータ破棄
protected override void UnloadContent()
{
 foreach (SceneBase sceneData in scene)
 {
  sceneData.UnloadContent();
 }
}

// 更新処理
public override void Update(GameTime gameTime)
{
 // キーボード入力更新
 m_KeyInput.Update();

 // シーンの初期化
 sceneToUpdate.Clear();
 // スタックに登録されているシーンをコピー
 foreach(SceneBase sceneBaseData in scene)
 {
  sceneToUpdate.Add(sceneBaseData);
 }

 // 画面のフォーカスフラグ
 bool isOtherHasForcus = false;
 // サブ画面表示フラグ
 bool isCoveredByOtherScene = false;

 // 登録しているシーン分ループ
 while(sceneToUpdate.Count > 0)
 {
  // 最上位のシーンから取得
  SceneBase currentScene = sceneToUpdate[sceneToUpdate.Count - 1];
  sceneToUpdate.RemoveAt(sceneToUpdate.Count - 1);

  // 取得したシーンの更新
  currentScene.Update(gameTime, isOtherHasForcus, isCoveredByOtherScene);

  // 画面がアクティブの場合
  if(currentScene.SceneState == SceneState.TransitionOn
   || currentScene.SceneState == SceneState.Active)
  {
   // フォーカスがある場合、キー入力を受け付ける

   // 次以降の画面では入力受付はしない
   isOtherHasForcus = true;
  }
  // サブ画面を持たない状態の場合
  if(!currentScene.IsPopUp)
  {
   // 次以降の画面は、現在の画面に覆われることになる
   isCoveredByOtherScene = true;
  }
 }
}

さて、マネージャで重要な処理を解説していきます。

// シーンの初期化
sceneToUpdate.Clear();
// スタックに登録されているシーンをコピー
foreach(SceneBase sceneBaseData in scene)
{
 sceneToUpdate.Add(sceneBaseData);
}

更新処理を行うためのSceneスタックを一度初期化し、
実際に保持しているSceneスタックをコピーします。

// 画面のフォーカスフラグ
bool isOtherHasForcus = false;
// サブ画面表示フラグ
bool isCoveredByOtherScene = false;

// 登録しているシーン分ループ
while(sceneToUpdate.Count > 0)
{
}

画面フォーカスは初回はトップ画面に値するためfalse
サブ画面も同様にfalseが初期値となっています。
そして先ほどコピーしたSceneスタックの個数分ループ処理を行います。

ループ処理の中身に入ります。

// 最上位のシーンから取得
SceneBase currentScene = sceneToUpdate[sceneToUpdate.Count - 1];
sceneToUpdate.RemoveAt(sceneToUpdate.Count - 1);

// 取得したシーンの更新
currentScene.Update(gameTime, isOtherHasForcus, isCoveredByOtherScene);

コピーしたSceneスタックの最後尾のSceneBaseを取得し、
スタックから削除します。
そして取得したSceneの更新処理を実行します。

// 画面がアクティブの場合
if(currentScene.SceneState == SceneState.TransitionOn
 || currentScene.SceneState == SceneState.Active)
{
 // フォーカスがある場合、キー入力を受け付ける
 
 // 次以降の画面では入力受付はしない
 isOtherHasForcus = true;
}
// サブ画面を持たない状態の場合
if(!currentScene.IsPopUp)
{
 // 次以降の画面は、現在の画面に覆われることになる
 isCoveredByOtherScene = true;
}

この取得したSceneがアクティブ、つまりメインとなる画面のシーンである場合、
その次に登録されているSceneたちはこの画面に覆われていることになるため、
isOtherHasForcusがtrueとなります。
同様にこのシーンでPopUp画面表示フラグがない場合には
その次に登録されているSceneたちはこの画面に覆われていることになるため、
isCoveredByOtherSceneがtrueとなります。

この処理をスタック分繰り返すことになります。

// 描画処理
public override void Draw(GameTime gameTime)
{
 foreach (SceneBase sceneBase in scene)
 {
  // シーンが非表示状態の場合、スキップ
  if (sceneBase.SceneState == SceneState.Hidden)
  {
   continue;
  }

  sceneBase.Draw(gameTime);
 }
}

描画処理関数はシンプルな構成です。
スタック分ループをまわし、取得したSceneの状態が非表示でなければ
そのシーンの描画処理を実行するっというだけです。

// シーン追加関数
public void AddScene(SceneBase sceneBase, PlayerIndex? controllerIdx)
{
 sceneBase.ControllerIndex = controllerIdx;
 sceneBase.GameManagerRef = this;
 sceneBase.IsExiting = false;

 // 画面がコンテンツを読み込む
 if (isInitialize == true)
 {
  sceneBase.LoadContent();
 }
 // シーン情報の登録
 scene.Add(sceneBase);
}

各SceneたちからManagerに向かって他のSceneを追加する処理となります。
追加されたシーンはスタックの最後尾に登録されていくため、
更新処理では真っ先に処理されることになります。

// シーン削除関数
public void RemoveScene(SceneBase sceneBase)
{
 // コンテンツを削除
 if (isInitialize == true)
 {
  sceneBase.UnloadContent();
 }
 // シーンの削除
 scene.Remove(sceneBase);
 sceneToUpdate.Remove(sceneBase);
}

今度はシーンデータ削除する処理です。
削除するシーンを引数で渡されてくるので
コンテントデータをアンロードし、
シーン保持用のスタックと更新用のスタック両方から
該当シーンデータを削除します。

// シーンスタック取得関数
public SceneBase[] GetSceneBase()
{
 return scene.ToArray();
}

おそらく使用用途はないかと思いますが
デバッグ向けの関数を追加しておきます。
現在保持しているシーン保持用のスタックデータをすべて取得する関数です。

現在登録されているシーンデータすべてを確認することができる・・・くらいかな?


シーンマネージャーは以上となります。

SceneBaseをしっかり作っておけばマネージャー側で管理することは以外と少ないのです。

最後にMainソース側での記述を

public class  ProjectName : Microsoft.Xna.Framework.Game
{
    GraphicsDeviceManager graphics;
   SceneManager sceneManager;

    public ProjectName()
    {
        graphics = new GraphicsDeviceManager(this);
    // 画面サイズ
        graphics.PreferredBackBufferWidth = 640;
        graphics.PreferredBackBufferHeight = 480;
        Content.RootDirectory = "Content";
        // タイトル
        Window.Title = "ゲームタイトル";

     sceneManager= new SceneManager(this);
     Components.Add(sceneManager);

     // 最初の画面をアクティブ
    sceneManager.AddScene(new Boot(), null);
    }


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

        // TODO: Add your drawing code here

        base.Draw(gameTime);
    }
}

Mainのクラスはこれだけ書いてあげるだけでOKとなります。
Components.Add(gameManager);
とすることで
sceneManagerがゲームコンポーネントとして登録され、
メインのループ処理を行うことができます。

以上でシーン管理の内容を終わります。

次はLoading画面ですかね〜




---------------------------
追記:
ひとつぬけていた部分がありました。
次回更新時にそれを説明します。

やっちゃったなぁ=

ラベル:シーン管理 C# XNA
posted by ヒイロ at 00:01| 福岡 ☀| Comment(0) | TrackBack(0) | プログラム | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:

認証コード: [必須入力]


※画像の中の文字を半角で入力してください。

この記事へのトラックバック
×

この広告は1年以上新しい記事の投稿がないブログに表示されております。