アイエスエス株式会社 "Innovative System Solutions"
徘徊アプリの動作骨組(1)
●黒猫徘徊アプリの実装
前回,CCatWndクラスの大枠部分の実装ができました.
開発目標である「黒猫徘徊」部分の実装を,CCatWndManagerの派生クラスを作って実装していきます.
●猫の状態で場合分け
CCatWndには複数のアニメーションパターンを登録できます.
当然,現在のアニメーション選択状況によって,やりたいことが変わってきます.
例えば,
- アプリ起動時には特定のアニメーションで猫が出現.
- 徘徊時はウィンドウ位置を都度動かす必要がある.
- 徘徊中止状態も欲しい…
等です.そのような処理分岐の記述が,圧倒的な長さを誇るif elseif… またはswitch case case… みたいなことになっていると,コードを見た瞬間に個人的に萎えるので,
「アニメーションパターン毎に状態クラスを用意」しておいて,これを切り替えることで猫の状態を遷移させることにします.
世間では「Stateパターン」とか呼ぶかもしれません.
猫状態の基本クラスを用意し,各状態クラスを派生させて作ります.基本クラスは↓のようなものです.
01
//DesktopMascotState
02
class
CDMState
03
{
04
public
:
05
virtual
~CDMState(){}
06
07
public
:
08
virtual
bool
OnCreate( CCatWnd *pCatWnd ) = 0;
09
10
virtual
void
OnSelect( CCatWnd *pCatWnd,
class
CDMManager *pManager ) = 0;
11
12
virtual
bool
OnPreChangeAnimFrame( CCatWnd *pCatWnd,
class
CDMManager *pManager, unsigned
int
AnimID, unsigned
int
FrameIndex ){
return
true
; }
13
virtual
bool
OnPopupMenu( CCatWnd *pCatWnd,
class
CDMManager *pManager,
UINT
ItemID ){
return
false
; }
14
15
virtual
bool
OnOtherEvent( CCatWnd *pCatWnd,
class
CDMManager *pManager,
UINT
message,
WPARAM
wParam,
LPARAM
lParam ){
return
false
; }
16
};
徘徊アプリで状態毎に異なる処理が必要そうなタイミングに呼ぶための処理関数群が宣言されています.宣言順に上から
- 初期処理用(アニメーションの登録等)
- 状態選択されたとき用(アニメーションの切換え等)
- フレーム切り替わり時用(アニメーションが1周したら他の状態に移行とか)
- ポップアップメニューのイベントに対する処理用
- その他のメッセージ処理用
といった感じです.
(コードを書き忘れるとアニメーション切換えが起らなくなってしまう類のメソッドを純粋仮想関数にしてあります.)
●CCatWndManager派生クラスの実装
CCatWndManagerから派生させたクラスには,以下のように…
01
class
CDMManager :
public
CCatWndManager
02
{
03
public
:
04
bool
ChangeState( CCatWnd *pCatWnd, unsigned
int
indx );
//m_CurStateIndexを書き換える
05
06
private
:
07
unsigned
int
m_CurStateIndex;
//現在の状態を選択するための値
08
std::vector< CDMState * > m_StatePool;
//状態群.インスタンス生成時あたりで状態群をつくって保持しておく.
09
bool
IsValidStateIndex( unsigned
int
indx )
const
{
return
( indx < m_StatePool.size() ); }
10
CDMState *GetCurState(){
return
m_StatePool.at( m_CurStateIndex ); }
11
};
状態クラス群を保持していて,必要なタイミングでChangeState()で切り替えて使用します.
あとは適切なタイミングで
GetCurState()->XXX();
として今の状態クラスに処理を任せるようにしておけばOK.
例えば,フレーム画像変更時の処理は,
01
//フレーム変更直前時
02
bool
CDMManager::OnPreChangeAnimFrame(
class
CCatWnd *pCatWnd, unsigned
int
AnimID, unsigned
int
FrameIndex )
03
{
04
if
( IsValidStateIndex(m_CurStateIndex) )
05
{
06
return
GetCurState()->OnPreChangeAnimFrame( pCatWnd,
this
, AnimID, FrameIndex );
07
}
08
else
09
{
10
return
true
;
11
}
12
}
として,現在の状態クラスに処理を投げるだけです.
猫ウィンドウクラスを作る(5)
- 2009-07-26 (日)
- 技術
●利用側で機能拡張できるようにする
CCatWndを使う側では,追加したメニュー項目選択時の処理や,猫ウィンドウが特に処理を行っていないメッセージに対して処理を追加したいはずなので,それができるようにしておきます.
●追加処理オブジェクトを指定させる
機能拡張のために「CCatWndを継承させる」というのでもいいのですが,今回はなんとなく
「CCatWndにコールバック関数を設定しておいて,WndProc()の中でこの関数をコールしてやる」という類の方針を取りました.
コールバック関数の型は
bool AdditionalWndProc( class CCatWnd *pCatWnd, UINT message, WPARAM wParam, LPARAM lParam );
みたいな.
実際には以下のように,関数ではなく,そういう関数をメンバにもつクラスのオブジェクトを指定する形になっていますが,意味は一緒です.
01
//猫ウィンドウとともに使う外部処理IF
02
//アプリはこれを継承したクラスを作り,猫ウィンドウにセットして使う.
03
class
CCatWndManager
04
{
05
public
:
06
virtual
~CCatWndManager(){}
07
08
public
:
09
//追加イベント処理関数.
10
//猫ウィンドウは,内部で必要なメッセージに対応する処理を行ったあとで,
11
//ウィンドウプロシージャ内(の最後らへん)でこの関数をコールする.
12
//(ただし,こないメッセージもある.
13
// WM_RBUTTONUPはポップアップに使われるので来ない.
14
// あと,猫ウィンドウのアニメーション用タイマのイベントとかも)
15
//
16
//メッセージを処理したらtrueを,処理しない場合はfalseを返すこと.
17
//(::DefWindowProc()をコールする類の処理は猫ウィンドウが行う)
18
virtual
bool
AdditionalWndProc(
class
CCatWnd *pCatWnd,
UINT
message,
WPARAM
wParam,
LPARAM
lParam ){
return
false
; }
19
};
CCatWnd側には設定用の関数を用意します.
void SetCatManager( CCatWndManager *pCatManager );
CCatWnd側は,設定がなされていれば,WndProc()内から設定されたオブジェクトのAdditionalWndProc()を呼びます.
CCatWnd側で既定の処理があるメッセージの際にも呼ぶか?ということについては考え方次第かもしれませんが,とりあえず今は↑の注釈にあるように,
「呼ばれないメッセージもある」という,「じゃあ何のメッセージのときは呼ばれるのか」が把握できないなんとも嫌な感じに仕上げてみました.
しかしこれだと,アニメーション選択に最もふさわしいであろうタイミングである「アニメーションフレームを切り替える時」に呼ばれなくなってしまうので,CCatWndManagerに
1
//アニメーションのフレーム切換え前にコールされる.
2
//引数は今の(切換え前の)アニメーションのAnimIDとFrameIndex.
3
//
4
//trueを返すとデフォルト動作(次のフレームに進める)処理が行われる.
5
//デフォルト動作を行わせないときはfalseを返すこと.
6
virtual
bool
OnPreChangeAnimFrame(
class
CCatWnd *pCatWnd, unsigned
int
AnimID, unsigned
int
FrameIndex );
とかいう特別な関数が追加されたりとかグダグダな感じになっています……なんだかなぁ.
●CCatWndに必要なメソッドを追加する
これでCCatWndManager(を継承したクラス)からCCatWndを操作できるので,アプリケーション独自の処理を追加できます.
ただし,今のままでは「CCatWndに対してできること」が少なすぎる(メニュー項目の追加とアニメーションの選択くらいしかない)ので満足に操作できません.CCatWndに今後publicなメソッドを増やしていく必要がありそうです.
CCatWndにウィンドウハンドルを返す関数を持たせれば,かなり自由に操作できるようになりますが,あまりにも自由すぎて「SetWindowLongでウィンドウプロシージャ置き換えたよ!」とか言われても困るので,現時点ではウィンドウハンドルは外部に教えない方針です.
とりあえず現在の最終目標は「黒猫徘徊ソフト」なので,徘徊に必要そうな機能(ウィンドウ位置を返す関数やウィンドウを移動する関数等)から順次追加されている最中です.
づづく...