EternalWindows
再描画 / ポイント判定

今回紹介するPtInRectは、指定した座標が長方形内にあるかどうかを調べます。 ゲーム等で選択肢やアイテムをマウスなどで選択するような場合、 そのアイテムの位置がRECT型で表現されているならば、この関数が役に立ちます。

BOOL PtInRect(
  CONST RECT *lprc,
  POINT pt
);

lprcは、初期化されたRECT構造体のアドレスを指定します。 ptは、座標を格納します。 戻り値は、ptで指定された座標がlprc内にあるならばTRUEが、 そうでないならばFALSEが返ります。 POINT構造体は、以下のように定義されています。

typedef struct tagPOINT { 
  LONG x; 
  LONG y; 
} POINT, *PPOINT;

メンバから分かるように、それぞれX位置とY位置を表しています。 この構造体は、マウス関連のプログラムでよく使われます。

今回のプログラムは、PtInRectによるポイント判定を行います。

#include <windows.h>

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR      szAppName[] = TEXT("sample");
	HWND       hwnd;
	MSG        msg;
	WNDCLASSEX wc;

	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = 0;
	wc.lpfnWndProc   = WindowProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hinst;
	wc.hIcon         = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = szAppName;
	wc.hIconSm       = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	
	if (RegisterClassEx(&wc) == 0)
		return 0;

	hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
	if (hwnd == NULL)
		return 0;

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	static RECT rc[3] = {0};

	switch (uMsg) {

	case WM_CREATE: {
		int i;
		int y = 50;

		for (i = 0; i < 3; i++) {
			SetRect(&rc[i], 50, y, 120, y + 30);
			y += 40;
		}

		return 0;
	}

	case WM_LBUTTONDOWN: {
		int   i;
		POINT pt;
		TCHAR szBuf[256];

		pt.x = LOWORD(lParam);
		pt.y = HIWORD(lParam);

		for (i = 0; i < 3; i++) {
			if (PtInRect(&rc[i], pt)) {
				wsprintf(szBuf, TEXT("%d 番目の長方形"), i + 1);
				MessageBox(hwnd, szBuf, TEXT("OK"), MB_OK);
			}
		}

		return 0;
	}

	case WM_PAINT: {
		int         i;
		HDC         hdc;
		PAINTSTRUCT ps;

		hdc = BeginPaint(hwnd, &ps);

		for (i = 0; i < 3; i++)
			Rectangle(hdc, rc[i].left, rc[i].top, rc[i].right, rc[i].bottom);

		EndPaint(hwnd, &ps);

		return 0;
	}

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

まず、判定用のRECT構造体を用意しなくてはなりません。 これらは、変数の宣言時に初期化してもよいのですが、 一定間隔の配置とするので動的に初期化します。 初期化といえば、WM_CREATEです。

case WM_CREATE: {
	int i;
	int y = 50;

	for (i = 0; i < 3; i++) {
		SetRect(&rc[i], 50, y, 120, y + 30);
		y += 40;
	}

	return 0;
}

(50, 50)の位置を最初の長方形とし、以下Y方向に40ずつの隙間を持ちます。 ポイント判定はWM_LBUTTONDOWNで行います。

case WM_LBUTTONDOWN: {
	int   i;
	POINT pt;
	TCHAR szBuf[256];

	pt.x = LOWORD(lParam);
	pt.y = HIWORD(lParam);

	for (i = 0; i < 3; i++) {
		if (PtInRect(&rc[i], pt)) {
			wsprintf(szBuf, TEXT("%d 番目の長方形"), i + 1);
			MessageBox(hwnd, szBuf, TEXT("OK"), MB_OK);
		}
	}

	return 0;
}

POINT構造体はXとYを表せるので、単純にlPramを基に初期化できます。 3つの長方形のどれが押されたのかは配列の添え字で求まります。 今回のプログラムでは、3つのメッセージのfor分の条件式として3という値が 使われていますが、本来ならばこの3という値は定数宣言するか、 sizeofで算出した方が好ましいでしょう。


戻る