« オーバーレイアイコンが表示されない件の対策まとめ | トップページ | 【C】固定小数点数三角関数 »

2010年8月 7日 (土)

【JavaSE】多角形との当たり判定

多角形との当たり判定のサンプルプログラム。

多角形の頂点の数は任意だが閉じていなければならない。

辺が交差すると内外判定が逆転するので注意。

当たり判定は整数演算のみで除算を使用しない。

カーソルキーで顔マークの移動。

顔マークは鼻の1ピクセルが当たり判定の対象。

PolyCollision.java

import isle.videogame.*;
import isle.videogame.image.*;
import java.awt.*;

@SuppressWarnings("serial")
public class PolyCollision extends VGStretchCanvas
{
	static final int SCREEN_WIDTH  = 240;
	static final int SCREEN_HEIGHT = 240;

	int[][] points = {
		{  40,   0, -32,  23,  12, -38,  12,  38, -32, -23 },
		{ -15,  50, -15, 110,  15, 110,  15,  50 },
		{ -15, 140, -15, 200,  15, 200,  15, 140 },
		{ -15, -50, -15,-110,  15,-110,  15, -50 },
		{ -15,-140, -15,-200,  15,-200,  15,-140 },
		{  50, -15,  90, -15,  90,  15,  50,  15 },
		{ 115, -15, 135, -15, 135,  15, 115,  15 },
		{ 160, -15, 200, -15, 200,  15, 160,  15 },
		{ -50, -15, -90, -15, -90,  15, -50,  15 },
		{-115, -15,-135, -15,-135,  15,-115,  15 },
		{-160, -15,-200, -15,-200,  15,-160,  15 },
	};

	Point[][] ptPoly;

	double angle;

	static final int FACE_WIDTH  = 32;
	static final int FACE_HEIGHT = 32;
	VGImage[] imgFace;
	Point ptFace;
	int nFace;

	static final Point ptDistance = new Point(-10000, -1);

	public PolyCollision()
	{
		super(SCREEN_WIDTH, SCREEN_HEIGHT, 60);
		setBackground(Color.DARK_GRAY);

		int i, j;

		Image img = getResourceImage("face.png");
		imgFace = new VGImage[2];
		imgFace[0] = new VGImage(img, FACE_WIDTH*0, 0, FACE_WIDTH, FACE_HEIGHT);
		imgFace[1] = new VGImage(img, FACE_WIDTH*1, 0, FACE_WIDTH, FACE_HEIGHT);
		ptFace = new Point();
		ptFace.x = SCREEN_WIDTH /2;
		ptFace.y = SCREEN_HEIGHT/2;

		ptPoly = new Point[points.length][];
		for (i=0; i<ptPoly.length; ++i) {
			ptPoly[i] = new Point[points[i].length/2];
			for (j=0; j<ptPoly[i].length; ++j) {
				ptPoly[i][j] = new Point();
			}
		}
	}

	@Override
	protected void frameUpdate(int skipped)
	{
		int i, j;

		// 回転角度を更新
		angle += Math.PI / 360;
		// 回転する中心座標を計算
		int cx = 120 + (int)(Math.cos(angle/4)*100);
		int cy = 120 + (int)(Math.sin(angle/4)*100);
		// 回転する多角形の頂点座標を計算
		for (i=0; i<ptPoly.length; ++i) {
			for (j=0; j<ptPoly[i].length; ++j) {
				ptPoly[i][j].x = (int)(Math.cos(angle)*points[i][j*2+0]+Math.sin(angle)*points[i][j*2+1])+cx;
				ptPoly[i][j].y = (int)(Math.cos(angle)*points[i][j*2+1]-Math.sin(angle)*points[i][j*2+0])+cy;
			}
		}

		int pad_states = getPadStates();
		// 十字方向に顔マーク移動
		if ((pad_states & (1<<PAD_LEFT))  != 0) --ptFace.x;
		if ((pad_states & (1<<PAD_RIGHT)) != 0) ++ptFace.x;
		if ((pad_states & (1<<PAD_UP))    != 0) --ptFace.y;
		if ((pad_states & (1<<PAD_DOWN))  != 0) ++ptFace.y;
		// 顔マーク移動後座標補正
		if (ptFace.x < 0) ptFace.x = 0;
		if (ptFace.y < 0) ptFace.y = 0;
		if (ptFace.x >= SCREEN_WIDTH)  ptFace.x = SCREEN_WIDTH-1;
		if (ptFace.y >= SCREEN_HEIGHT) ptFace.y = SCREEN_HEIGHT-1;

		nFace = 0;
		for (i=0; i<ptPoly.length; ++i) {
			if (isCollidePoly(ptFace, ptPoly[i])) nFace = 1;
		}
	}

	boolean isCollidePoly(Point pt, Point[] ptPolygon)
	{
		int j;
		int cCrossed = 0;
		int k = ptPolygon.length - 1;
		for (j=0; j<ptPolygon.length; ++j) {
			// 交差する辺の数を数える
			if (isCrossSegment(pt, ptDistance, ptPolygon[k], ptPolygon[j])) ++cCrossed;
			k = j;
		}
		// 奇数なら多角形の内側
		return (cCrossed & 1) != 0;
	}

	boolean isCrossSegment(Point p1, Point p2, Point p3, Point p4)
	{
		// p1,p2を結ぶ線分とp3,p4を結ぶ線分が交差しているときtrue
		int a1 = (p1.x-p2.x) * (p3.y-p1.y) + (p1.y-p2.y) * (p1.x-p3.x);
		int a2 = (p1.x-p2.x) * (p4.y-p1.y) + (p1.y-p2.y) * (p1.x-p4.x);
		if ((a1 < 0 && a2 > 0) || (a1 > 0 && a2 < 0)) {
			int a3 = (p3.x-p4.x) * (p1.y-p3.y) + (p3.y-p4.y) * (p3.x-p1.x);
			int a4 = (p3.x-p4.x) * (p2.y-p3.y) + (p3.y-p4.y) * (p3.x-p2.x);
			if ((a3 < 0 && a4 > 0) || (a3 > 0 && a4 < 0)) return true;
		}
		return false;
	}

	@Override
	protected void frameStretchRender(Graphics g)
	{
		int i, j;

		g.setColor(Color.BLACK);
		g.fillRect(0,0,SCREEN_WIDTH,SCREEN_HEIGHT);

		imgFace[nFace].paint(g, ptFace.x-(FACE_WIDTH-1)/2, ptFace.y-(FACE_HEIGHT-1)/2, 0);

		g.setColor(Color.RED);
		for (i=0; i<ptPoly.length; ++i) {
			int k = ptPoly[i].length - 1;
			for (j=0; j<ptPoly[i].length; ++j) {
				g.drawLine(ptPoly[i][k].x, ptPoly[i][k].y, ptPoly[i][j].x, ptPoly[i][j].y);
				k = j;
			}
		}
	}

	public static void main(String[] args)
	{
		new VGFrame(new PolyCollision(), 640, 480, "多角形と当たり判定");
	}
}

|

« オーバーレイアイコンが表示されない件の対策まとめ | トップページ | 【C】固定小数点数三角関数 »

JavaSE」カテゴリの記事

コメント

コメントを書く



(ウェブ上には掲載しません)




« オーバーレイアイコンが表示されない件の対策まとめ | トップページ | 【C】固定小数点数三角関数 »