第51章 簡易アニメーション その2


第50章では子供ウィンドウの作り方を解説しました。 今回はこの子供ウィンドウにBMPを表示してウィンドウごと 動かします。まずは例を見てください。

子供ウィンドウに表示されたBMPが左右に移動します。 親ウィンドウの大きさを変えてもはじまで進めば逆戻りします。 子供ウィンドウのフレームはないのでちょっと見ただけでは 子供ウィンドウが移動しているとはわかりません。 実際はもっとスムースに動きます。非力なマシンでも大丈夫です。

では、どのようにすればよいのでしょうか。答は簡単です。 MoveWindow関数を使えばウィンドウを動かすことができます。

BOOL MoveWindow( HWND hWnd, // ウィンドウハンドル int X, int Y, int nWidth, // ウィンドウの幅 int nHeight, // 高さ BOOL bRepaint // 再描画フラグ );

子供ウィンドウに対してこの関数を使う場合X,Yは親ウィンドウの クライアント領域の左上が0,0となります。

再描画フラグがTRUEのときはウィンドウに対してWM_PAINTメッセージが 送られ再描画が行われます。

では、具体的に作り方をを見てみましょう。

まず、適当なBMPを用意します。ここではいつもおなじみの

を使うことにします。

VC++5.0では、次のようにしてビットマップリソースを作ります。 用意したBMPを適当なグラフィックソフトで読み込み クリップボードにコピーします。もとのBMPはあらかじめ256色以下に 減色しておきます。

「ビットマップの新規作成ボタン」を押します。何も表示されていない 所をダブルクリックすると「ビットマップのプロパティ」ダイアログが出てきますので IDを"MYBMP"、色を256色にします。「編集」「貼り付け」でクリップボードの BMPをはりつけます。このときデフォルトでは48*48の大きさなので これを大きくするか聞かれます。はい、と答えるとBMPの大きさに拡大されます。 「ファイル」「名前を付けて保存」で*.rcで保存します。ここではソースファイルの 名前にあわせてchd02.rcとしました。「プロジェクト」「プロジェクトへ追加」 「ファイル」でchd02.rcを選びます。 これで、ビットマップリソースが使えるようになりました。他の環境でも 似たような方法で実行できると思います。また、ビットマップだけなら 自分でリソーススクリプトを書いても簡単です。

///////////////////////////////////////////////////////////////// // // Bitmap // MYBMP BITMAP DISCARDABLE "mybmp.bmp"

たったこれだけです。 では、ソースファイルchd02.cppを見てみましょう。

// chd02.cpp #define STRICT #define ID_MYTIMER 32767 #define ID_MYCHILD 100 #define CHD_W 79 #define CHD_H 77 #include <windows.h> LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); LRESULT CALLBACK ChildProc(HWND, UINT, WPARAM, LPARAM); BOOL InitApp(HINSTANCE, WNDPROC, LPCTSTR); BOOL InitInstance(HINSTANCE, int, LPCTSTR); int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow) { MSG msg; LPCTSTR szClassName = "chd02"; //ウィンドウクラス if (!hPrevInst) { if (!InitApp(hCurInst, WndProc, szClassName)) return FALSE; } if (!InitInstance(hCurInst, nCmdShow, szClassName)) { return FALSE; } while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return (int)msg.wParam; }



あらかじめBMPの大きさをBMP_W, BMP_Hなどでdefineしておけば 後の作業が楽です。子供ウィンドウの大きさもこれに合わせます。 当然SetTimer関数を使うのでタイマーIDも定義しておきます。 WinMain関数の中身は前章とほとんど同じです。

//ウィンドウ・クラスの登録 BOOL InitApp(HINSTANCE hInst, WNDPROC WndProc, LPCTSTR szClassName) { WNDCLASS wc; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; //プロシージャ名 wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst; //インスタンス wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH); wc.lpszMenuName = NULL; //メニュー名 wc.lpszClassName = (LPCSTR)szClassName; return (RegisterClass(&wc)); }

ウィンドウクラスの登録関数も前章と同じです。 子供ウィンドウのクラス登録にも使えるよう引数が少し多くなっています。

//ウィンドウの生成 BOOL InitInstance(HINSTANCE hInst, int nCmdShow, LPCTSTR szClassName) { HWND hWnd; hWnd = CreateWindow(szClassName, "親ウィンドウ", //タイトルバーにこの名前が表示されます WS_OVERLAPPEDWINDOW, //ウィンドウの種類 CW_USEDEFAULT, //X座標 CW_USEDEFAULT, //Y座標 CW_USEDEFAULT, //幅 CW_USEDEFAULT, //高さ NULL, //親ウィンドウのハンドル、親を作るときはNULL NULL, //メニューハンドル、クラスメニューを使うときはNULL hInst, //インスタンスハンドル NULL); if (!hWnd) return FALSE; ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); return TRUE; }

親ウィンドウの作成関数はいつもと同じです。

//ウィンドウプロシージャ LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) { RECT rc; static int x, y; static int direction; int id; static HWND hChdWnd; HINSTANCE hInst; enum {right, left}; switch (msg) { case WM_CREATE: hInst = ((LPCREATESTRUCT)lp)->hInstance; InitApp(hInst, ChildProc, "child"); hChdWnd = CreateWindow("child", NULL,//タイトルバーにこの名前が表示されます WS_CHILD, //ウィンドウの種類 0, //X座標 0, //Y座標 CHD_W, //幅 CHD_H, //高さ hWnd, //親ウィンドウのハンドル、親を作るときはNULL (HMENU)ID_MYCHILD,//メニューハンドル、子供のID hInst, //インスタンスハンドル NULL); ShowWindow(hChdWnd, SW_SHOW); UpdateWindow(hChdWnd); SetTimer(hWnd, ID_MYTIMER, 100, NULL); break; case WM_TIMER: GetClientRect(hWnd, &rc); MoveWindow(hChdWnd, x, y, CHD_W, CHD_H, TRUE); switch (direction) { case right: x += 10; if (x >= rc.right - CHD_W) direction = left; break; case left: x -= 10; if (x < 0) direction = right; break; } break; case WM_CLOSE: id = MessageBox(hWnd, (LPCSTR)"終了してもよいですか", (LPCSTR)"終了確認", MB_YESNO | MB_ICONQUESTION); if (id == IDYES) { DestroyWindow(hWnd); } break; case WM_DESTROY: KillTimer(hWnd, ID_MYTIMER); PostQuitMessage(0); break; default: return (DefWindowProc(hWnd, msg, wp, lp)); } return 0L; }

子供ウィンドウのX,Y座標や進む方向(右に進むか、左に進むか)をstaticな 変数として宣言しています。また、right, leftをenumで宣言しています。

WM_CREATEメッセージを受け取ったら子供ウィンドウを作成します。 このときウィンドウのタイトルとか枠はいらないので、ウィンドウスタイルは WS_CHILDのみです。また、子供ウィンドウを作ったらSetTimer関数を 実行します。どのくらいの頻度でWM_TIMERメッセージを発行するかは 実際に動かしてみてから決めて下さい。

WM_TIMERメッセージを受け取ったらまず、親ウィンドウの 大きさを調べます。アニメーション実行中にユーザーの気まぐれで 親ウィンドウの大きさが変えられるかもしれないからです。 次に子供ウィンドウをX,Yに移動します。進んでいる方向により Xの増減をします。ここでは移動距離を10としました。 この数値もいろいろ変えてみてください。そして、右に移動中であれば X座標が(親ウィンドウの横幅−BMP_W)より大きくなれば親の右端に到達した ことになります。その時は移動方向を左向きにします。 左に移動中の場合も同様の考えでプログラムを書きます。 ここでは、簡単のために移動方向が右、左の2方向しか設定しませんでしたが、 上下、斜め方向の移動にも挑戦してみてください。 場合分けさえしっかりできれば簡単です。 (こういうところはWindowsのプログラムというよりはDOSのプログラムと同じです)

アプリケーション終了時には忘れずにタイマーを殺しておいてください。 忘れやすい人は、SetTimer関数を書いたらすぐにKillTimer関数を書くのを 習慣にしてしまえばよいですね。

つぎは、子供ウィンドウのプロシージャですが、BMP表示だけです。

LRESULT CALLBACK ChildProc(HWND hChdWnd, UINT msg, WPARAM wp, LPARAM lp) { HBITMAP hBitmap; HINSTANCE hInst; PAINTSTRUCT ps; HDC hdc, hdc_mem; switch (msg) { case WM_PAINT: hInst = (HINSTANCE)GetWindowLong(hChdWnd, GWL_HINSTANCE); hdc = BeginPaint(hChdWnd, &ps); hBitmap = LoadBitmap(hInst, "MYBMP"); hdc_mem = CreateCompatibleDC(hdc); SelectObject(hdc_mem, hBitmap); BitBlt(hdc, 0, 0, CHD_W, CHD_H, hdc_mem, 0, 0, SRCCOPY); DeleteDC(hdc_mem); DeleteObject(hBitmap); EndPaint(hChdWnd, &ps); break; default: return (DefWindowProc(hChdWnd, msg, wp, lp)); } return 0L; }

これは、説明不要ですね。動かす子供ウィンドウを複数作ってみるのも おもしろいものです。

[SDK Index] [総合Index] [Previous Chapter] [Next Chapter]

Update Jun/26/1997 By Y.Kumei
当ホーム・ページの一部または全部を無断で複写、複製、 転載あるいはコンピュータ等のファイルに保存することを禁じます。