今回は、結構前のメモで作った状態管理用のクラスを、どんなゲームでも使いまわし出来るように変更します。
前回作ったクラスは、GameStatesというenumを使用して各シーンを管理していましたが、その場合他のゲームで使う場合は新しくenumを書き直す必要があったり、他の型(例えばstring型)を使って管理しようとした場合変更するのが面倒です。なので今回はC#のジェネリックを使用し、使いまわし出来るようにします。ジェネリックは簡単に言うとContent.Load<ジェネリック>(path)
で「ジェネリック」と書いてある部分です。これにより、使用時に使う型を決められるので、柔軟性が出来ます。
まず、GameStateManagerクラスを変更します。ジェネリックを使用したクラスは、クラス名の宣言後に<T>みたいにジェネリックを使用していることを表さなくてはいけません(<>の中身はTである必要はありません。好きに変更できますが、その場合下にあるコードのTの部分を全て新しい名前に変更してください)。
class GameStateManager<T>
{
T m_currentState;
...
そして、クラス内で、GameStatesのenumを使用していた箇所を全て`T`に置き換えてください。
次に、GameStateクラスをジェネリックを使用するように変更します。クラスの宣言部だけでなく、GameStateManagerを使用している部分もGameStateManagerから、GameStateManager<T>に変更してください。
abstract class GameState<T>
{
GameStateManager<T> manager; // このクラスを保存しているクラス
...
public GameState(Game game, GraphicsDeviceManager graphics, GameStateManager<T> owner)
{
this.game = game;
this.graphics = graphics;
this.content = game.Content;
this.spriteBatch = new SpriteBatch(graphics.GraphicsDevice);
this.manager = owner;
}
public GameStateManager<T> Manager
{
get { return manager; }
}
// ...その他のコード
次に、またGameStateManagerクラスを変更します。今度は、GameStateクラスを使用している部分を、GameState<T>に変更します。このクラスは、全体的に変更部分が多いので、クラスの殆どを乗せたコードを下に張っときます。
class GameStateManager<T>
{
Dictionary<T, GameState<T>> m_states;
T m_currentState;
public GameStateManager()
{
m_states = new Dictionary<T, GameState<T>>();
}
public T CurrentState
{
get { return m_currentState; }
set { m_currentState = value; }
}
public void AddState(GameState<T> toAdd, T type)
{
m_states.Add(type, toAdd);
}
public bool RemoveState(T toRemove)
{
return m_states.Remove(toRemove);
}
public void ChangeState(T newState)
{
m_currentState = newState;
}
public void Update(GameTime gameTime)
{
GameState<T> state;
//現在のstateがある場合、保存されているGameStateへの参照を得る
if (m_states.TryGetValue(m_currentState, out state))
{
// 現在のシーンを更新
state.Update(gameTime);
}
}
次に、MenuStateクラスを変更します。変更部分は、GameStateクラスを継承するときに、どの型を使ってシーンを切り替えるかを表すのと、GameStateManagerにも同じ様に型を表します。今回は、string型を使用してシーンの管理をします。
class MenuState : GameState<string>
{
public MenuState(Game game, GraphicsDeviceManager graphics, GameStateManager<string> owner)
: base(game, graphics, owner)
{
// ... 初期化処理
}
public override void Update(GameTime gameTime)
{
// ..メニュー画面の更新処理
// メニューで選択された場合
if (Input.IsPressed(Keys.Enter))
{
// 別のシーンへ移動
Manager.ChangeState("Play");
}
}
後は、Game1クラス内で、実際に各シーンとマネージャを使用する箇所を変更するだけです。
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
GameStateManager<string> manager;
...
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
// 新しいシーンを足す
manager = new GameStateManager<string>();
manager.AddState(new MenuState(this, graphics, manager), "Menu");
}
以上です。少し説明が飛びがちで解り難かったかも知れませんが、ジェネリックを使用したほうが、ライブラリ化する時とかに再利用性が高まるのでオススメです。ですが、解りやすいコードをとことん目指すのであれば、string型や、int型を使用するだけでも十分です。