点と矩形の当たり


概要

点と矩形の判定は当たり判定の中で簡単な部類に入ります。
ゲーム内ではマウスポインタやスマホのタップした座標と
ボタンや敵等の当たり判定で使用します。

注意点

この記事内の座標軸はX軸「右が正、左が負」Y軸「下が正、上が負」で書いています。

矩形

矩形とは全ての角が直角(90度)の四角形(長方形)のことです。

common_0001

矩形の作り方

4つの点の内で対角の2点の座標を用意することが出来たら矩形の作成ができます。
※これ以降で紹介する計算はX軸は右が正で左が負、
 Y軸は下が正で上が負の座標軸で行っています。

common_0002

上図の矩形の座標でわかるように左上と右下、右上と左下などの
対角の点の座標が分かれば残りの点の座標の割り出しができます。

矩形の点は矩形の原点座標と矩形の縦と横のサイズから割り出すことができ、
原点座標は「左上」「中央」「中央下」のいずれかにすることが多いです。

左上

左上はDirectXなどを使用したプログラムで多く使用される原点の位置です。

common_0003

原点が左上の場合、そのまま左上を2点の内の1点として扱い、
もう1点は対角の右下となります。
右下の点は原点である左上の座標に矩形のサイズを足すことで割り出せます。

float x = 100.0f;
float y = 30.0f;
float width = 80.0f;
float height = 40.0f;

printf("左上座標 (x, y) = (%.1f, %.1f)\n", x, y);
printf("右下座標 (x, y) = (%.1f, %.1f)\n", x + width, y + height);


// 実行結果
左上座標 (x, y) = (100.0f, 30.0f)
右下座標 (x, y) = (180.0f, 70.0f)

中央

中央の場合は対角となる2点を決めてから割り出しを行います。
DirectXなどでは左上と右下の2点で行うことが多いので、
今回はそちらの2点を使用した説明をします。

common_0004

原点が中央の場合は、矩形のサイズを2で割った結果を使用します。
結果の反映は左上の場合はX、Yともに原点から引いて、
右下の場合は原点から足します。

float x = 100.0f;
float y = 30.0f;
float width = 80.0f;
float height = 40.0f;

float half_width = width / 2.0f;
float half_height = height / 2.0f;

printf("左上座標 (x, y) = (%.1f, %.1f)\n", x - half_width, y - half_height);
printf("右下座標 (x, y) = (%.1f, %.1f)\n", x + half_width, y + half_height);


// 実行結果
左上座標 (x, y) = (60.0f, 10.0f)
右下座標 (x, y) = (140.0f, 50.0f)

中央下

中央下の場合も対角となる2点を決めてから割り出しを行うので、
中央と同じで左上と右下の2点を使用した説明をします。

common_0005

中央下の場合X軸は矩形のサイズを2で割った値を
左の点は原点から引いて、右の点は原点から足します。
Y軸は上の点は原点から縦のサイズ分だけ引いて、
下の点は原点の値をそのまま使用します。

float x = 100.0f;
float y = 30.0f;
float width = 80.0f;
float height = 40.0f;

float half_width = width / 2.0f;

printf("左上座標 (x, y) = (%.1f, %.1f)\n", x - half_width, y - height);
printf("右下座標 (x, y) = (%.1f, %.1f)\n", x + half_width, y);


// 実行結果
左上座標 (x, y) = (60.0f, -10.0f)
右下座標 (x, y) = (140.0f, 30.0f)

判定方法

判定方法は点が矩形の中にあるかどうかを調べます。
具体的には点が矩形の左辺と右辺の間にあり、
なおかつ上辺と下辺の間なら当たりとなります。

collision_0014

判定条件

点が矩形のX軸、Y軸の辺の間にあるということを証明する条件は
以下の内容が全て成立している場合です。

collision_0015

実装

以下でプログラムで実装する方法の例を紹介します。
※紹介する実装方法が絶対ではありません。

単純な実装

プログラムで点と矩形の当たりを実装するには「点の座標(X、Y)」「矩形の原点座標(X、Y)」
「矩形のサイズ(横、縦)」が必要なので、それらを変数として用意して判定を行います。
また、矩形の原点の位置がオブジェクト毎に異なるなら変数を用意して
判別できるようにしますが、今回の原点の位置は左上で固定するので変数は用意しません。

#include <stdio.h>
#include <math.h>

void main(void)
{
	// 点の情報
	float point_pos_x = 120.0f;
	float point_pos_y = 50.0f;
			
	// 矩形の情報
	float rect_pos_x = 100.0f;
	float rect_pos_y = 30.0f;
	float rect_width = 80.0f;
	float rect_height = 40.0f;

	// 点が矩形の中にあるかチェック
	if (point_pos_x >= rect_pos_x && point_pos_x <= (rect_pos_x + rect_width) &&
		point_pos_y >= rect_pos_y && point_pos_y <= (rect_pos_y + rect_height) )
	{
		printf("当たってる\n");
	}
	else
	{
		printf("当たってない\n");
	}

	getchar();
}

当たり情報をまとめる

当たり判定は敵の数分など判定対象が複数用意されることが基本です。
そのため、先ほどの例のように毎回点や矩形の当たり判定情報を用意するために
変数を宣言するのは非常に手間になっていきます。
そこで、構造体やクラスにまとめて管理することで変数宣言の手間を省きます。

#include <stdio.h>
#include <math.h>
	
// 矩形データ
typedef struct
{
	float m_PosX;		// X座標
	float m_PosY;		// Y座標
	float m_Width;		// 横幅
	float m_Height;		// 縦幅
} Rect;

// 点データ
typedef struct
{
	float m_PosX;		// X座標
	float m_PosY;		// Y座標
} Point;

void main(void)
{
	// 点の情報
	Point point = {
		20.0f,
		40.0f,
	};
			
	// 矩形の情報
	Rect rect = {
		100.0f,
		30.0f,
		80.0f,
		40.0f,
	};


	// 点が矩形の中にあるかチェック
	if (point.m_PosX >= rect.m_PosX && point.m_PosX <= (rect.m_PosX + rect.m_Width) &&
		point.m_PosY >= rect.m_PosY && point.m_PosY <= (rect.m_PosY + rect.m_Height) )
	{
		printf("当たってる\n");
	}
	else
	{
		printf("当たってない\n");
	}

	getchar();
}

関数化する

当たり判定は様々な場所で使用する可能性があります。
その時に毎回上のコードを書いていたら手間なので
関数化しておいて簡単に使えるようにしておいた方が作業時間を短縮できます。

OnCollisionPointAndRect
内容 PointとRectのデータを使用し、当たり判定チェックをする
点と矩形が当たっていればtrue、当たていなければfalseを返す
戻り値 bool型で当ったていればtrue、
当たっていなければfalseを返す
引数の型 説明
Point 判定用点データ
Rect 判定用矩形データ

定義

bool OnCollisionPointAndRect(Point point, Rect rect)
{
	// 点が矩形の中にあるかチェック
	if (point.m_PosX >= rect.m_PosX && point.m_PosX <= (rect.m_PosX + rect.m_Width) &&
		point.m_PosY >= rect.m_PosY && point.m_PosY <= (rect.m_PosY + rect.m_Height) )
	{
		return true;
	}

	return false;
}

使用例

#include <stdio.h>
#include <math.h>

// 矩形データ
typedef struct
{
	float m_PosX;		// X座標
	float m_PosY;		// Y座標
	float m_Width;		// 横幅
	float m_Height;		// 縦幅
} Rect;

// 点データ
typedef struct
{
	float m_PosX;		// X座標
	float m_PosY;		// Y座標
} Point;

bool OnCollisionPointAndRect(Point point, Rect rect)
{
	// 点が矩形の中にあるかチェック
	if (point.m_PosX >= rect.m_PosX && point.m_PosX <= (rect.m_PosX + rect.m_Width) &&
		point.m_PosY >= rect.m_PosY && point.m_PosY <= (rect.m_PosY + rect.m_Height) )
	{
		return true;
	}

	return false;
}

void main(void)
{
	// 点の情報
	Point point = {
		20.0f,
		40.0f,
	};
			
	// 矩形の情報
	Rect rect = {
		100.0f,
		30.0f,
		80.0f,
		40.0f,
	};

	if (OnCollisionPointAndRect(point, rect) == true)
	{
		printf("当たってる\n");
	}
	else
	{
		printf("当たってない\n");
	}

	getchar();
}