24
2022
06

分离轴碰撞检测

最近用python写小游戏用到碰撞检测,矩形碰撞检测不够用了,简单加一个多边形碰撞检测,不好接物理引擎。自己写一个。

参考:https://blog.csdn.net/you_lan_hai/article/details/108429268

function satTest(shape1, shape2)
	return satTestHalf(shape1, shape2) and satTestHalf(shape2, shape1)
end

function satTestHalf(shape1, shape2)
	for edge in shape1的所有边 do
		axis = 获得edge的垂线
		range1 = 将 shape1 的所有顶点投影到axis上,得到最大和最小值
		range2 = 将 shape2 的所有顶点投影到axis上,得到最大和最小值
		if range1 与 range2 无交集 then
			return 不相交
		end
	end
	return 相交
end

伪代码写得好,直接翻译就行。原理也看得明白。

效果如下:

获得 Adobe Flash Player

代码:

package  {

	import flash.display.Graphics;
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Point;
	import flash.text.TextField;

	public class Main extends MovieClip {

		public var tf: TextField;
		public var p00: MovieClip;
		public var p01: MovieClip;
		public var p02: MovieClip;
		public var p03: MovieClip;
		public var p10: MovieClip;
		public var p11: MovieClip;
		public var p12: MovieClip;
		public var p13: MovieClip;
		public var pArr0: Array = [];
		public var pArr1: Array = [];
		public function Main() {
			// constructor code
			pArr0 = [p00, p01, p02, p03];
			pArr1 = [p10, p11, p12, p13];
			for (var i: int = 0; i < 4; i++ ) {
				drag(pArr0[i]);
				drag(pArr1[i]);
				pArr0[i].tf.text = i;
				pArr1[i].tf.text = i;
				pArr0[i].mouseChildren = false;
				pArr1[i].mouseChildren = false;
			}
			refresh();
			addEventListener(Event.ENTER_FRAME, enterframeHandler);
		}

		private function enterframeHandler(e:Event):void 
		{
			if (!draged) {
				return;
			}
			refresh();
			tf.text = checkCollision() ? "碰撞了" : "";
			draged = false;
		}

		private function checkCollision(): Boolean {

			return satTest(toPoints(pArr0), toPoints(pArr1));
		}

		private function toPoints(arr: Array): Array {
			var result: Array = [];
			for (var i: int = 0; i < arr.length; i++) {
				result[i] = new Point(arr[i].x, arr[i].y);
			}
			return result;
		}

		private function drawAxis(axis: Array, color: uint): void {
			var p0: Point = axis[0];
			var p1: Point = getAxisPoint(axis, -1000);
			var p2: Point = getAxisPoint(axis, 1000);
			drawSeg(p1, p2, color, 0.5);
			//drawPoint(p0, color);
			//drawPoint(p1, color);
			//drawPoint(p2, color);
		}

		private function drawSeg(p0: Point, p1: Point, color: uint, alpha: Number = 1.0): void {
			graphics.lineStyle(1, color, alpha);
			graphics.moveTo(p0.x, p0.y);
			graphics.lineTo(p1.x, p1.y);
		}

		private function drawPoint(p: Point, color: uint): void {
			graphics.lineStyle(1, color);
			graphics.drawCircle(p.x, p.y, 5);
		}

		private function getAxisPoint(axis: Array, n: Number): Point {
			var p0: Point = axis[0];
			var p1: Point = axis[1];
			return new Point(p0.x + (p1.x - p0.x) * n, p0.y + (p1.y - p0.y) * n);
		}

		private function drawAxisPoint(axis: Array, n: Number, color: uint): void {
			var p2: Point = getAxisPoint(axis, n);
			drawPoint(p2, color);
		}

		private function drawRange(axis: Array, range: Array, color: uint): void {
			drawAxisPoint(axis, range[0], color);
			drawAxisPoint(axis, range[1], color);
		}

		// https://blog.csdn.net/you_lan_hai/article/details/108429268
		private function satTest(points0: Array, points1: Array): Boolean {
			return satTestHalf(points0, points1) && satTestHalf(points1, points0);
		}

		private function satTestHalf(points0: Array, points1: Array): Boolean {
			var n: Number = points0.length;
			for (var i: int = 0; i < n; i++) {
				var axis: Array = getAxis(points0[i], points0[(i + 1) % n]);
				var range1 = getRange(axis[0], axis[1], points0);
				var range2 = getRange(axis[0], axis[1], points1);

				drawAxis(axis, 0x0000ff);
				drawRange(axis, range1, 0x00ff00);
				drawRange(axis, range1, 0xff0000);

				if (!intersection(range1[0], range1[1], range2[0], range2[1])) {
					return false;
				}
			}
			return true;
		}

		private function getAxis(p0: Point, p1: Point): Array {
			var p2: Point = new Point((p0.x + p1.x) / 2, (p0.y + p1.y) / 2);
			var p3: Point = new Point(-(p0.y - p1.y), p0.x - p1.x);
			p3.normalize(1);
			return [p2, add(p2, p3)];
		}

		private function middle(p0: Point, p1: Point): Point {
			return new Point((p0.x + p1.x) / 2, (p0.y + p1.y) / 2);
		}

		private function add(p0: Point, p1: Point): Point {
			return new Point(p0.x + p1.x, p0.y + p1.y);
		}

		private function subtract(p0: Point, p1: Point): Point {
			return new Point(p0.x - p1.x, p0.y - p1.y);
		}

		private function getRange(p0: Point, p1: Point, points: Array): Array {
			var max: Number = -Infinity;
			var min: Number = Infinity;
			var v: Point = subtract(p1, p0);
			for (var i: int = 0; i < points.length; i++ ) {
				var p: Point = points[i];
				var n: Number = dot(v, subtract(p, p0));
				if (n < min) {
					min = n;
				}
				if (n > max) {
					max = n;
				}
			}
			return [min, max];
		}

		private function dot(p0: Point, p1: Point): Number {
			return p0.x * p1.x + p0.y * p1.y;
		}

		private function intersection(n0: Number, n1: Number, n2: Number, n3: Number): Boolean {
			if (n1 < n2 || n3 < n0) {
				return false;
			}
			return true;
		}

		private function refresh(): void {
			graphics.clear();
			draw(graphics, pArr0, 0x00ff00);
			draw(graphics, pArr1, 0xff0000);
		}

		private function draw(gra: Graphics, arr: Array, co: uint): void {
			gra.lineStyle(1, co);
			gra.moveTo(arr[0].x, arr[0].y);
			for (var i: int = 1; i < arr.length; i++ ) {
				gra.lineTo(arr[i].x, arr[i].y);
			}
			gra.lineTo(arr[0].x, arr[0].y);
		}

		//-----------------------------
		private var curSp:Sprite;
		private var draged:Boolean = false;

		private function drag(sp:Sprite):void
		{
			sp.buttonMode = true;
			sp.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownHandler);
		}

		private function mouseDownHandler(e:MouseEvent):void
		{
			curSp = e.currentTarget as Sprite;
			curSp.startDrag();
			addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
			addEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
			addEventListener(Event.MOUSE_LEAVE, mouseUpHandler);
		}

		private function mouseUpHandler(e:MouseEvent):void
		{
			curSp.stopDrag();
			curSp = null;
			removeEventListener(MouseEvent.MOUSE_MOVE, mouseMoveHandler);
			removeEventListener(MouseEvent.MOUSE_UP, mouseUpHandler);
			removeEventListener(Event.MOUSE_LEAVE, mouseUpHandler);
		}

		private function mouseMoveHandler(e:MouseEvent):void
		{
			//
			draged = true;
		}
	}

}

源码打包下载

« 上一篇下一篇 »

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。