次のページ 前のページ 目次へ
10. ユーザーコントロールの扱い
前章では牌の描画まで行ってみましたが、
あれだけでは画面が切り替わったり、
上にインデックス画面を開いた後に、描いた画像が消えてしまいます。
ここではユーザーコントロールを使ってその問題を解決してみます。
10.1 利用前の各種設定
まずは、何はさておきクラスライブラリのヘルプを開いてください。
ヘルプには各コントロールに必要な初期設定や制限事項、
使い方のサンプルが載っており、非常に参考になります。
これを見て正しく設定しなくては満足に動きませんし、
仮に動いたとしても見えないバグを生みますから、要チェックです!
ユーザーコントロールであれば以下のような初期設定・使い方が必要になります。
- コールバック関数を設定する
- コールバック関数ではメッセージをチェックしてから処理する
- コールバック関数は必ず true を返す
- フォームのセットアップ関数で UC_CALL_BACK_WORK_PID に AplForm を設定する
それでは、以下でこれらを順々に説明していきましょう。
10.2 コールバック関数の設定
コールバック関数というのは、
その名の通りシステム側から何かあった時に呼び出してもらう
ユーザー独自の処理の事で、SZABの場合フォームエディタから設定しておきます。
GameFormを開いて GameAreaUCを選択し、
プロパティの UC_CALL_BACK_PID を編集してください。
鉛筆のアイコンを押して関数名を決め、
紙のようなアイコンでテキストエディタを開き、
コールバック関数を記述します。
[コールバック関数 初期状態]
BOOLEAN GameAreaUC_CB(MESSAGE* Message, WINDOWHD WnHandle, RECTANGLE* UserRect, void** Work)
{
}
|
ところで、
上のダイアログで設定した関数名と、
エディタで開いたソース中の関数名は、必ず一致させねばなりません。
そうでないとビルドの途中で『関数が存在しない』と怒られてしまいます。
ボタン等のイベント関数は良いのですが、
こうしたコールバック関数では注意しましょう。
10.3 コールバック関数の記述の仕方
この関数には、ユーザーコントロールが受けたメッセージのうち、
ヘルプに書かれてある2つを除いた全てが毎回まわってきています。
従って、本当に自分(GameAreaUC)宛ての物なのかの判断や、
この領域の描き直しも含めて、
メッセージを識別して処理する必要があります。
この辺りは、前にイベントドリブンの処理の例としてあげたように、
switch文による振り分けが一般的でしょう。
[例]
switch( Message->CommonMsg.MessageID ) {
case RedispID: /* 再描画 */
break;
case PenDownID: /* ペンが押された */
break;
case PenStillDownID: /* ペンが押され続けている */
break;
case PenUpID: /* ペンが離された */
break;
}
10.4 コールバック関数の戻り値
制限事項を見ると、必ず true を戻さないといけません。
これは何も考えずに、
関数から呼び出し元へ戻るところで次の1文を実行させてやりましょう。
なお、これを忘れると、処理の状態により動いたり動かなかったり、
原因が特定しづらくなります。
戻り値を戻す関数では、十分注意してください。
[例] return( true );
10.5 フォームセットアップで行うべき処理
ちょっとピンと来ないかも知れませんが、
各コントロールは私たちの書くコードの外にいるため、
基本的にはこちらの情報へのアクセス手段がありません。
しかし当然ながらそれじゃ使い物になりませんので、
ユーザーコントロールが持つ UC_CALL_BACK_WORK_PID に、
こちらの情報構造体へのポインタ AplForm を設定しておき、
ポインタ経由でアクセスさせるわけです。
この設定は、フォームとコントロールが出来上がった直後で、
まだイベントメッセージが配送される前に行わねばなりません。
つまり、FormSetup()内に書く必要があるのです。
具体的には、
フォーム部品(フォームエディタで一番下にある部品)を選び、
プロパティの FormSetUp を選んで
コールバック関数を設定してください。
[例]
void GameFormSetUp(APLFORMPTR AplForm, MESSAGE* Message)
{
/* ユーザーコントロールの Work 変数に値を設定する(必須) */
SetCtrlProperty( GameAreaUC, UC_CALL_BACK_WORK_PID,
pValue, AplForm, AplForm->Control );
}
ここで設定しておいた値は、
コールバック関数の第4引数 void **Work で渡されますので、
次のようにコールバック関数の中で AplForm = *Work のようにして読み出します。
Workそのものではなく*Workとして間接参照している点に注意してください。
BOOLEAN GameAreaUC_CB(MESSAGE* Message, WINDOWHD WnHandle, RECTANGLE* UserRect, void** Work)
{
APLFORMPTR AplForm; /* ローカル変数の宣言 */
AplForm = *Work; /* ローカル変数AplFormに値を入れる */
/* ・・・後はコールバック関数の処理 */
return( true ); /* 必ず true で戻る */
}
10.6 再描画イベントの目的
ここからは、コールバック関数の中身に入って説明しましょう。
目に見えるコントロールの場合、
絶対に必要になるのがこの『再描画処理』です。
前に DispImageRes() を使った時、確かに牌を描いたはずなのに、
上に何か表示すると消えてしまうという現象が起きました。
これは、先の例が一時的に画面に絵を描いただけに過ぎず、
『消えたので描き直して欲しい』という要求を誰も処理していない為です
(このシステムからの要求こそが、再描画イベントです)。
大体のウィンドウ・システムではこんな感じで、
『自分が描いた内容はシステムの要求でいつでも復元できるように』
しておかなくてはなりません。
ボタン等の各コントロールは自前で再描画処理を持っているため、
こちらで何もしなくても自分を描画してくれますが、
ユーザーコントロールの場合は、そうした処理はないので
コールバック関数のcase RedispID: のところに
再描画処理を書いてあげるわけです。
10.7 ユーザーコントロールの描画
コールバック関数にはコントロールの領域を示す UserRect が引数として渡されますので、
これから範囲を得て描画を行います。
四川省では背景の描画と牌の描画とがありますが、
まだゲーム本体まで作り込んでいないので、
とりあえず背景を塗りつぶした後、ALLDRAW()関数を呼ぶように作っておきます
(今のところ1個の牌を表示してるだけですが、
実際のゲームではここを拡張すれば済みます)。
BOOLEAN GameAreaUC_CB(MESSAGE* Message, WINDOWHD WnHandle, RECTANGLE* UserRect, void** Work)
{
APLFORMPTR AplForm;
POINT Start,End;
COLOR fg;
/* AplForm ポインタの値を 引数のWork から得る */
AplForm = *Work; /* 必ずFormSetup()内で UC_CALL_BACK_WORK_PID 設定済のこと! */
switch( Message->CommonMsg.MessageID ) {
case RedispID:
/* ユーザーコントロールの左上の座標と右上の座標を Start, End に設定 */
Start.x = UserRect->left;
Start.y = UserRect->top;
End.x = UserRect->left + UserRect->width;
End.y = UserRect->top + UserRect->height;
/* 凹んだ感じの枠を描画 */
DrawFrame( WnHandle, &Start, &End, GT_NORMAL,
NORM_GCTABLESET, 2*4, DF_HOLLOWED, FSET, NULL );
/* 2 [dot] ずつ内側に領域を狭める */
Start.x += 2*4;
Start.y += 2*4;
End.x -= 2*4;
End.y -= 2*4;
/* 塗りつぶす色の設定 */
SetColorRGB( &fg, 0, 102, 51 );
SetColorInfo( &fg, 0 );
/* 狭めた矩形領域の塗りつぶし */
AreaClear( WnHandle, &Start, &End, &fg, FSET, NULL );
/* 後は、牌を読み込みながら描く */
ALLDRAW();
break;
}
return( true );
}
void ALLDRAW( void )
{
RECTANGLE myrect;
RESOURCE myres;
myrect.left = 230 * 4; /* 4を掛けるのを忘れずに! */
myrect.top = 180 * 4; /* (同上) */
strcpy( myres.ResFile, "WST3IM00" ); /* リソースブロック */
myres.ResID = souzu1ID; /* 末尾に ID が必要! */
DrawImageRes( AplForm->FormWnHandle, &myrect, 0, &myres, NORM_SCALE, NULL );
}
さて、ここまで終わったら、再度ビルドして実際の動作を確かめてみてください。
再描画イベントへの対応を行ったことで、インデックス等を開いた後でも
ちゃんと牌が表示されているはずです。
コントロールの背景も奇麗に塗りつぶされていますね。
次章ではグローバル変数とEXECワークの使い方を学びつつ、
実際のゲームと同様、画面配列変数 G[][] の内容に基づいた牌描画などを行いたいと思います。
次のページ 前のページ 目次へ