2月, 2012 のアーカイブ

今回は、結構前のメモで作った状態管理用のクラスを、どんなゲームでも使いまわし出来るように変更します。

前回作ったクラスは、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型を使用するだけでも十分です。

 

今回は、前回紹介したのとは逆で、キーが話された時のみtrueを返すメソッドの実装方法をメモります。

実装方法は前回と同じで前のフレームと現在のフレームの入力状態を保存し、それらの状態を確認します。違う所は、確認方法が前回とは逆で、前のフレームでは入力があり、現在のフレームでは入力が無い状態でのみtrueを返すメソッドを実装します。

「こんなの何時使うんだー!」と思うかもしれませんが、あると便利なときもあります。一つの例としては、何かをドラッグ&ドロップするようなゲームを作る場合(World of Gooみたいな)、キーを離した瞬間に処理を行うことを簡単に出来るようになります。

実装は前回同様、前のフレームのキー状態と、現在のキー状態を保存する変数を宣言します。

KeyboardState m_KeyState;     // 現在の状態
KeyboardState m_prevKeyState; // 前のフレームの状態

またまた前回と同じで、Update()メソッドが呼ばれるたびにキーの状態を更新します。

m_prevKeyState = m_KeyState;
m_KeyState = Keyboard.GetState();

次に確認をするメソッドですが、上で説明したとおり、ここは前回と少し違います。

public bool IsReleased(Keys key)
{
return ( m_KeyState.IsKeyUp(key) && m_prevKeyState.IsKeyDown(key) );
}

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

if ( IsReleased( Keys.Escape ) )
{
// ... 離された時の処理
}

以上です。

今回は、他のメモでよく出てきたキーの押された瞬間を獲得する(押されたフレームのみtrueを得る)方法をメモります。

実装方法は、一つ前のフレームでの入力状態を保存し、確認の際には前のフレームで任意のキーが押されておらず、現在のフレームで押されている場合はtrueを返すようにします。これにより、押されたフレームのみtrueを得ることが出来、一度キーを話さない限りfalseを返すメソッドを実装します。

実装するにはまず、前のフレームのキー状態と、現在のキー状態を保存する変数を宣言します。

KeyboardState m_KeyState;     // 現在の状態
KeyboardState m_prevKeyState; // 前のフレームの状態

次に、Update()メソッドが呼ばれるたびにキーの状態を更新します。

m_prevKeyState = m_KeyState;
m_KeyState = Keyboard.GetState();

入力を確認するメソッドを実装します。内用は上で説明したのと同じで、前のフレームでは押されておらず、現在のフレームでは押されている状態のみ、trueを返します。

public bool IsPressed(Keys key)
{
return (m_KeyState.IsKeyDown(key) && m_prevKeyState.IsKeyUp(key));
}

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

if ( IsPressed( Keys.Escape ) )
{
   // ... 入力されたときの処理
}

以上です。

今回はあまりのネタの無さに、昔別サイトの掲示板で書いた物を書き直して見ました。このメモは、前に書いた2Dカメラの作り方と同じ論理でC++とdirectx9で2D用のカメラの作り方を乗せます。ちなみに、今回は描画を全てID3DXSpriteを使用していますので、板ポリゴンを使用する場合は別の方法をとってください。

まず、違う記事で書いた2Dカメラを持ってきます。実装方法は、XNAをdirectxに変換するだけなので、詳細は省いときます。必要なのはカメラの現在地、ズーム(拡大)、回転を合わせたマトリックスを出す関数を用意することです。

次に実際に描画する時、ID3DXSpriteのBegin()を呼ぶ際にカメラの行列を保存し、End()が呼ばれるまでの間に描画される全てのスプライトにこのマトリックスを適用す。

後は、描画処理を実装するだけです。少し自分で作ったクラスが入りますが、描画関数はXNAのSpriteBatchに似せて作られています:

// カメラのマトリックスを保存する
D3DXMATRIX _transform = camera.TransformMatrix;

void Sprite::Draw(Texture *texture, Vector2 &position, Colour &colour, Vector2 &origin, float scale, float angle, float depth)
{
// 位置と原点を保存する
D3DXMATRIX m1, m2;
D3DXVECTOR3 ori( origin.X, origin.Y, 1.0f );
D3DXVECTOR3 pos( position.X, position.Y, 0 );

// スケーリング
D3DXMatrixScaling( &m1, scale, scale, 0.0f );

// 回転
D3DXMatrixRotationZ( &m2, angle );
D3DXMatrixMultiply( &m1, &m1, &m2 );

// 移動
D3DXMatrixTranslation( &m2, position.X, position.Y, depth );
D3DXMatrixMultiply( &m1, &m1, &m2);

// パラメータで送られてきた位置や角度とカメラのマトリックスを合わせる
D3DXMatrixMultiply( &m1, &m1, &_transform);

// 行列をスプライトに設定する
HR( _sprite->SetTransform( &m1 ) );

// 実際に描画する
_sprite->Draw( texture, NULL, &ori, NULL, colour );

// 行列をリセットする
D3DXMatrixIdentity( &m1 );
_sprite->SetTransform( &m1 );
}

以上です。実装後はこんな感じになります:

http://www.youtube.com/watch?v=b_6AWmiFz54

今回は、前回紹介したコントローラーの振動方法に、タイマーを付けて自動的に振動を止める方法をメモります。

基本的にすることは、振動中はタイマーに経過時間を足し、設定した時間以上立っていれば振動を止めるだけです。

実装するにはまず、振動しているかを表すフラグ、タイマー、そして振動させる時間を保存する変数を宣言します。

float m_timer = 0.0f;            // 振動用のタイマー
bool m_isVibrating = false;      // 振動しているかどうか
float m_vibrationLength = 0.35f; // 振動している時間

次に、振動させ、タイマーなどを初期化する関数を作ります。この関数の第3引数は、何秒コントローラーを振動させるかを表します。

private void SetVibration(float left, float right, float length)
{
 GamePad.SetVibration(PlayerIndex.One, left, right);
 m_isVibrating = true;
 m_timer = 0.0f;
 m_vibrationLength = length;
}

次に、Update()メソッド内に、タイマーを更新し、止めるかどうかの判断をする処理を実装します。している事は、上で説明した物です。

// もし振動中なら
if (m_isVibrating)
{
 // タイマーに経過時間を足す
 m_timer += (float)gameTime.ElapsedGameTime.TotalSeconds;

 // タイマーが設定した時間になった時
 if (m_timer >= m_vibrationLength)
 {
 // 振動を止める
 GamePad.SetVibration(PlayerIndex.One, 0.0f, 0.0f);
 }
}

後は、SetVibration()メソッドを呼び、実際に振動させるだけです。

KeyboardState keystate = Keyboard.GetState();

if (keystate.IsKeyDown(Keys.Z))
{
 // コントローラー1を振動させる
 SetVibration(0.5f, 0.5f, 0.35f);
}

以上です。今回はコントローラー1に限定した実装なので、複数のコントローラーを使ったり、柔軟性を持たせたい場合は、変数を配列にしたり、SetVibration()メソッドにPlayerIndexを渡すようにしたりする事により実装できます。