次のページ 前のページ 目次へ

9. 牌の描画処理を作ろう

まだ何の働きもしないものの、とりあえずmoreソフトを作るところまできました。 まずは口当たりのいいところで、牌データの作成とその描画処理からやってみましょう。

9.1 牌のイメージデータ作成

前に書いたように、 牌の画像はフルカラーBMPファイルとして作成しておき、 ビューエディタでmoreソフト用データに変換します。 moreで扱える画像データにはアイコンデータとイメージデータの2通りがあり、 次のような違いがあります。 今回使う牌の画像程度であればGUIカラー256色でも十分ですが、 縁起物という事で 16bit color(6万色) のデータとして作ってみます。 データ量という観点からは 16bit color の物は 256色データの 2倍になりますので、 あまり大きな物は扱えませんが、まあいいでしょう。

では、ビューエディタを起動して、牌を1つずつ切り出して保存していきましょうか。 手順は次のようになります。

イメージの切り出し
  1. ビューエディタを起動
  2. [ファイル][減色処理の設定] で 『減色処理を行わない』に設定
  3. [ファイル][新規作成] で新規ビューの作成を行う
  4. [ファイル][イメージの読み込み] で牌の画像ファイルを読み込む
  5. 切り出したい範囲の左上をクリックし、次に範囲の右下をクリック
  6. [編集][切り取り] で1つの牌だけを切り取る
  7. リソースID を入力
  8. (16bit color で保存したい場合、オプションに 5 を入力)
  9. [ファイル][上書き保存]
  10. 種類(アイコンタイプ:*.icn, イメージタイプ:*.ima)を選ぶ
  11. ファイル名を付け保存


例えばソウズの1番目なら、リソースID=souzu1, オプション=5, ファイル名=souzu1.ima のようにし、 後は1つ1つの牌につき、上の 3 〜 11 を繰り返して 36個の牌すべてについて ビューを作成していけば良いわけです。リソースIDの付け方は、 他と重複しない名称で、それなりにわかり易ければいいでしょう。

※重要

ファイルに保存する場合、その保存場所には1つ注意が必要です。 通常は、プロジェクト・ディレクトリの view 以下に保存しますが、 これだとフォーム部品で使われていないデータは全くリソース生成されません。 従って、上のようにプログラム中から読み込むデータを強制的にリソース生成させるには、 view の下に次のようなサブディレクトリを作り、 その中にファイルを保存する必要があります (prjdirはプロジェクトを格納しているディレクトリを指します)。
	アイコン:	prjdir\view\****IC00\
	イメージ:	prjdir\view\****IM00\
ファイル名 souzu1.ima なら、prjdir\view\wst3IM00\souzu1.ima に保存するわけです。

※補足説明(ビューエディタの減色処理)

アイコン及び256色イメージデータは、 GUIカラーと呼ばれる特定の256色で作成しなければなりません。 ビューエディタで『減色処理を行う』に設定している場合、 イメージを読み込んだ時にGUIカラーに合わない色を 自動的に最も近い色に変換してくれます。 逆に、自然画像などをそのままの見た目で取り込みたい場合は、 『減色処理を行わない』にしないと色がくるいます。

9.2 牌の描画方法

上記で牌のイメージリソースが出来ましたので、 どれかボタンを押した時にでも画面に描画させてみましょうか。

ところで、イメージリソースを画面に表示するにはどうすれば良いのでしょう? こんな時はマニュアルを参照するのが一番です。 SZABからは [ヘルプ][開発関連ヘルプ][APIリファレンス]で、 オンラインヘルプを参照できますので、そちらを開いて見てみましょう。

ざっと眺めてみるとどうやら ZAURUS DRAW マネージャ を見れば良さそうですね。 ここで、どの関数を使えばいいか 関数仕様 で調べて、 設定する値や構造体などの詳細は 変数の型定義 など、 詳細説明の頁で調べるわけです。

今回リソースからのイメージ描画に用いるのはこれ↓

#include 

void DrawImageRes(WnHandle,Where,DispMode,ImageRes,Scale,Mask)

【機 能】  
指定イメージリソースを描画する。

【引き数】

WINDOWHD     WnHandle     描画対象ウィンドウハンドル
RECTANGLE    *Where       ウィンドウ内座標(左上)
CDSPMODE     DispMode     描画モード

FRAME_MASK_ON  マスクを使用する(マスク領域はMaskで指定)

0             マスクを使用しない

RESOURCE     *ImageRes    イメージリソース
SCALEMODE    Scale        倍率
             NORM_SCALE: 実サイズ(現在はこれのみサポート)

RECTANGLE    *Mask        マスク領域
	使用する時は、ModeにFRAME_MASK_ONを設定する必要がある。
	また使用しないときはこの値はNULLにしておいて下さい

【戻り値】  
なし

この関数は、 1番目の引数で描くウィンドウ(フォームと考えても良い)を、 2番目の引数でその場所を、 4番目の引数でどのリソースかを指定するものです。 とりあえずマスクは使わずに、座標 x=230, y=180 に表示するとすれば、 次のように書くわけですね(WnHandle は既に渡されているものと仮定)。

{
	RECTANGLE	myrect;
	RESOURCE	myres;

	myrect.left = 230 * 4;	/* 4を掛けるのを忘れずに! */
	myrect.top  = 180 * 4;	/* (同上) */

	strcpy( myres.ResFile, "WST3IM00" );	/* リソースブロック */
	myres.ResID	= souzu1ID;		/* 末尾に ID が必要! */

	DrawImageRes( WnHandle, &myrect, 0, &myres, NORM_SCALE, NULL );
}
C に慣れてない人は、なんで Where じゃなくて &myrect と書いているのか? なぜ Scale と書いてあったのに NORM_SCALE としたのか? と疑問に思うかも知れませんが、 WhereとかScaleと書いてあったのは仮引数だという事を思い出してください。 実際の関数呼び出しの際には関係ないんです。

むしろ、ここでは myrect ではなく &myrect と、 頭にアンパサンドをつけてる事に注目してください。 DrawImageRes()の第2引数に要求されるのは、 『RECTANGLE *Where』という事ですから、 RECTANGLE型のポインタを渡す必要があるのです。 ここでは RECTANGLE型の myrect 変数を用意しましたので、 そのアドレスを渡すために、 頭に & をつけて RECTANGLE型のポインタ ・・・つまり、RECTANGLE * 型として渡しているわけです。

それからもう1つ、座標を指定する際に 4を掛けていることにも注目ください。 オンラインヘルプの描画座標の頁に書いてありますが、 ザウルスの描画関数で座標を指定する場合、 ドットではなくQPという単位を用いる約束になっています。 1 [ドット] = 4 [QP] ですので、上の例でも 4 を掛けてドット座標から QP座標に変換しているというわけです。

9.3 ボタン押下時の処理方法

ボタンはクラスライブラリの物ですから、 先と同様にオンラインヘルプのクラスライブラリリファレンスを見ながら 作業を進めていきましょう。 枠固定型文字ボタンクラスの概要を開いてみると、 ボタンの概要やプロパティ、 イベントハンドラー関数などが詳しく説明されています。 使用例も具体的な例で参考になりますね。

※注意事項(必読)

各クラスごとの制限事項は必ずチェックしてください! ここには、そのクラスを使う上で絶対に必要な初期設定や、 必ず対応しなければならないイベントなどについて書かれています。 ここに書いてある事を守らない場合、満足な動作をしないばかりか 後々のトラブルの原因になりますので必ず守ってください。
さて、ボタンが押された際に任意の処理をする方法ですが、 これはその部品用のイベントハンドラー関数を書くことで行います。 枠固定型文字ボタンには、ButtonPressStartID, ButtonPressID, ButtonSelectID, ButtonStillSelectID の4つがありますが、普通にボタンを押した場合はこの内の ButtonSelectIDを使います。

では、フォームエディタを開いてください。 試しに説明ボタンが押されたら牌を描くようにしてみましょうか。 説明ボタンを選択して、画面右下のイベント・タグをクリックしてください。 さっきまでプロパティシートが表示されていた部分が、 イベントハンドラーシートに変わったはずです。

イベントハンドラーの設定

ここで ButtonSelectID のところをクリックし、 右端の鉛筆ボタンを押すとテキストエディタが開き、 このイベントに対応する処理を書くことができるわけです。 初期時は次のような内容で開くでしょう。
void GameHelpBtnBtnSelect(APLFORMPTR AplForm, MESSAGE* Message, const MESSAGE* BtnMsg)
{
}
それじゃ、この中に先程の牌の描画処理を組み込んでみましょうか。 ところで、ここで第1引数として渡される AplForm ですが、 これは各アプリケーション情報へのポインタとなっており、 moreソフトでは至るところで利用される重要な構造体です。 これの内容詳細は、クラスライブラリ・リファレンスの概説、 イベントハンドラー関数説明の共通事項 の所に詳しく書かれていますので、 適宜参照してください。

とりあえず今ここで必要なのは描画する場所 WINDOWHD型 の値です。 これは AplForm->FormWnHandle として得ることができますので、 先程の描画説明で WnHandle としていた部分をこれに代えて書いてみましょう。
void GameHelpBtnBtnSelect(APLFORMPTR AplForm, MESSAGE* Message, const MESSAGE* BtnMsg)
{
	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 );
}
これで、説明ボタンを押すとザウルスの画面右下の方に牌を1個描画できるようになったはずです。

9.4 moreソフトの終了の仕方

今のままではmore管理の画面から停止させるより他ないので、 とりあえずテスト用にmoreソフトを終了させる処理だけ追加しておきましょう。 先の説明ボタンと同様に、 終了ボタンの ButtonSelectID イベントハンドラーを開いてください。

通常、イベントハンドラには2つのメッセージが渡され、 ボタンの例では BtnMsg の方に付加情報が入ってくるようになっています。 大抵はこれだけで済みますが、 アプリケーション側からシステムに要求を出したい場合には、 もう1つの Message の方を利用しなければなりません。

具体的には、このMessageにAppliProcEndReqIDを入れ、 パラメータにAF_THIS_APPLI を指定してシステムに返すことで、 moreソフトが自分自身の終了要求を出すというわけです。
void GameEndBtnBtnSelect(APLFORMPTR AplForm, MESSAGE* Message, const MESSAGE* BtnMsg)
{
	Message->CommonMsg.MessageID = AppliProcEndReqID;
	Message->AppliProcEndReq.AplFilter = AF_THIS_APPLI;
}
上記のように書いた場合、 終了ボタンが押されるとシステムに AppliProcEndReqID を送り、 これにより Form表示状態からEXEC 状態、OUT 状態へと遷移し、 最後に EXIST状態にまで移行するというわけです。

9.5 ビルドして描画を確認する

うまく描画ができるか確認する必要もありますので、 ここらでビルドしてザウルス上で実行してみましょう。

ビルドした際、何かエラーは出たでしょうか? もしエラーが出た(赤い文字)場合は、それがどのファイルで起きたのか、 何行目で起きたのかを確認して、該当するソースファイルを修正しましょう。 ほんの 1文字の間違い、文末のセミコロン(;)の付け忘れでも、 Cでは何行にも渡ってエラーが表示される場合があります。 エラーを恐れずに1つ1つ解決していきましょう。 慣れるとそうそうはまりませんので :-)。

ザウルスで実行できるようになったら、 説明ボタンを押して牌が表示されるか、 終了ボタンを押してmoreソフトが終了できるか等、確認を取りましょう。

・・・ところで、牌を表示させた後にインデックスか何かを開いて閉じてみてください。 ボタンや四川省のタイトルはちゃんと出ているのに、 先程描いた牌は消えてしまってるはずです。 さて、なぜこうなるのでしょう。

実は、この辺りの問題を解決させる為に、 ユーザーコントロールを利用するんですね。 次章では、ユーザーコントロールの使い方を絡めて説明したいと思います。


次のページ 前のページ 目次へ