Flash/Flex学习笔记(41):碰撞检测

简介: 碰撞检测基本上可能分为二类:对象与对象的碰撞检测、对象与点的碰撞检测 为了方便测试,先写一个box类(生成一个小矩形) package { import flash.display.Sprite; public class Box extends Sprite { ...

碰撞检测基本上可能分为二类:对象与对象的碰撞检测、对象与点的碰撞检测

为了方便测试,先写一个box类(生成一个小矩形)

package {
	
	import flash.display.Sprite;
	
	public class Box extends Sprite {
		
		private var w:Number;
		private var h:Number;
		private var color:uint;
		public var vx:Number=0;
		public var vy:Number=0;
		
		public function Box(width:Number=50, height:Number=50, color:uint=0xff0000) {
			w=width;
			h=height;
			this.color=color;
			init();
		}
		
		public function init():void {
			graphics.beginFill(color);
			graphics.drawRect(-w / 2, -h / 2, w, h);
			graphics.endFill();
		}
	}
}

最基本的对象碰撞检测:hitTestObject

package {

	import flash.display.Sprite;
	import flash.events.Event;

	public class Boxes extends Sprite {

		private var box:Box;
		private var boxes:Array;
		private var gravity:Number=0.1;

		public function Boxes() {
			init();
		}

		private function init():void {
			boxes = new Array();
			createBox();
			addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}

		private function onEnterFrame(event:Event):void {
			box.vy+=gravity;
			box.y+=box.vy;

			//如果物体下落到了舞台(最下)边界,则再造一个出来往下掉
			if (box.y+box.height/2>stage.stageHeight) {
				box.y=stage.stageHeight-box.height/2;
				createBox();
			} else{
				for (var i:uint = 0; i < boxes.length; i++) {
					//每个正在下掉的物体与其它物体做(矩形)碰撞检测
					if (box!=boxes[i]&&box.hitTestObject(boxes[i])) {
						box.y=boxes[i].y-boxes[i].height/2-box.height/2;
						//堆到顶了,则停止
						if (box.y<=box.height/2){
							removeEventListener(Event.ENTER_FRAME,onEnterFrame);
						}
						else{
							createBox();
						}
					}
				}
			}
			
		}

		private function createBox():void {
			box=new Box(Math.random()*40+10,Math.random()*40+10,Math.random()*0xffffff);
			box.x=Math.random()*stage.stageWidth;
			addChild(box);
			boxes.push(box);
		}
	}
}

如果把Box换成前面例子中的Ball,就是下面这个样子:

很明显:矩形换成球后,碰撞检测变得不精确了,有一些球似乎并没有真正撞到其它球也停下来了,这是为什么腻?

img_55f2ce5ef6ceef874ba41f4f56a1086b.jpg

 

img_9858dd1833746bb4f62b70ba540f6ec1.jpg

答案就在于:Flash对象碰撞检测默认采用“对象的矩形边界”做为检测依据。上面二张图演示了这一细节:第一张图虽然肉眼看上去只有二个矩形相交了,但是在Flash看来,其实每对图形都碰到了(第二张图),所以大家应该也能明白为啥换成球后,有些球会浮在空中了。

对象与点的碰撞检测:hitTestPoint

package {
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.text.TextField;
	
	public class PointHitTest extends Sprite {
		private var ball:Ball;
		private var box:Box;
		private var txt:TextField = new TextField();
		
		public function PointHitTest() {
			init();
		}
		
		private function init():void {
			ball=new Ball;
			addChild(ball);
			ball.x=stage.stageWidth/2;
			ball.y=stage.stageHeight/2;
			
			box = new Box(90,90);
			addChild(box);
			box.x = 100;
			box.y = ball.y;
			
			addEventListener(Event.ENTER_FRAME,EnterFrameHandler);
			
			addChild(txt);
			txt.selectable = false;
		}
		
		private function EnterFrameHandler(event:Event):void {
			if (ball.hitTestPoint(mouseX,mouseY) || box.hitTestPoint(mouseX,mouseY)) {
				txt.text = "碰到了!";
			}
			else{
				txt.text = "";
			}
			txt.x = mouseX + 15;
			txt.y = mouseY;
		}
	}
}

用鼠标在二个物体上划过,会看到鼠标所在点与矩形及小球的碰撞检测结果,同样这里也存在一个问题:对于小球而言,默认也是采用矩形边界检测的,所以鼠标移到小球的边角时,虽然还没碰到球,也提示"碰到了",还好Flash提供了一个可选参数,以改进检测的精确度,只要把hitTestPoint第三个可选参数设置为true即可

if (ball.hitTestPoint(mouseX,mouseY) || box.hitTestPoint(mouseX,mouseY,true)) {

基于距离的检测:即检测二个物体的中心点距离是否低于最小距离

var ball_1:Ball=new Ball(70,0xff0000);
var ball_2:Ball=new Ball(70,0x0000ff);

ball_1.x=stage.stageWidth/2;
ball_1.y=stage.stageHeight/2;

ball_2.x=stage.stageWidth/2;
ball_2.y=stage.stageHeight/2;

ball_1.vx = Math.random()*20 - 20;
ball_1.vy = Math.random()*20 - 20;
ball_2.vx = Math.random()*20 - 20;
ball_2.vy = Math.random()*20 - 20;

addChild(ball_1);
addChild(ball_2);

addEventListener(Event.ENTER_FRAME,EnterFrameHandler);

function EnterFrameHandler(e:Event):void {
	ball_1.x+=ball_1.vx;
	ball_1.y+=ball_1.vy;
	ball_2.x+=ball_2.vx;
	ball_2.y+=ball_2.vy;
	CheckBoundary(ball_1);
	CheckBoundary(ball_2);

	var dx:Number=ball_1.x-ball_2.x;
	var dy:Number=ball_1.y-ball_2.y;
	var dist:Number=Math.sqrt(dx*dx+dy*dy);
	if (dist<(ball_1.radius + ball_2.radius)) {

		var angle:Number=Math.atan2(dy,dx);
		ball_1.vx=dist*Math.cos(angle)*0.1;
		ball_1.vy=dist*Math.sin(angle)*0.1;
		ball_2.vx=dist*Math.cos(angle)*-0.1;
		ball_2.vy=dist*Math.sin(angle)*-0.1;
	}
}

function CheckBoundary(b:Ball) {
	if (b.x>stage.stageWidth-b.width/2||b.x<=b.width/2) {
		b.x-=b.vx;
		b.vx*=-1;
	}

	if (b.y>stage.stageHeight-b.height/2||b.y<=b.height/2) {
		b.y-=b.vy;
		b.vy*=-1;
	}
}

很明显,这种方法对于圆形物体是十分精确的,但对于非规则形状,只能近似检测.

如果结合上二篇提到的弹性运动,可以做出更复杂的动画:

package {
	import flash.display.Sprite;
	import flash.events.Event;
	public class Bubbles extends Sprite {
		private var balls:Array;
		private var numBalls:Number=10;
		private var centerBall:Ball;
		private var bounce:Number=-1;
		private var spring:Number=0.2;
		public function Bubbles() {
			init();
		}
		private function init():void {
			balls=new Array  ;
			centerBall=new Ball(100,0xcccccc);
			addChild(centerBall);
			centerBall.x=stage.stageWidth/2;
			centerBall.y=stage.stageHeight/2;
			for (var i:uint=0; i<numBalls; i++) {
				var ball:Ball=new Ball(Math.random()*40+5,Math.random()*0xffffff);
				ball.x=Math.random()*stage.stageWidth;
				ball.y=Math.random()*stage.stageHeight;
				ball.vx=(Math.random()*2-1)*10;
				ball.vy=(Math.random()*2-1)*10;
				addChild(ball);
				balls.push(ball);
			}
			addEventListener(Event.ENTER_FRAME,onEnterFrame);
		}
		private function onEnterFrame(event:Event):void {
			for (var i:uint=0; i<numBalls; i++) {
				var ball:Ball=balls[i];
				move(ball);
				var dx:Number=ball.x-centerBall.x;
				var dy:Number=ball.y-centerBall.y;
				var dist:Number=Math.sqrt(dx*dx+dy*dy);
				var minDist:Number=ball.radius+centerBall.radius;
				if (dist<minDist) {
					var angle:Number=Math.atan2(dy,dx);
					var tx:Number=centerBall.x+Math.cos(angle)*minDist;//弹性运动的目标点x坐标
					var ty:Number=centerBall.y+Math.sin(angle)*minDist;//弹性运动的目标点y坐标
					ball.vx+=(tx-ball.x)*spring;
					ball.vy+=(ty-ball.y)*spring;
				}
			}
		}
		private function move(ball:Ball):void {
			ball.x+=ball.vx;
			ball.y+=ball.vy;
			if (ball.x+ball.radius>stage.stageWidth) {
				ball.x=stage.stageWidth-ball.radius;
				ball.vx*=bounce;
			} else if (ball.x-ball.radius<0) {
				ball.x=ball.radius;
				ball.vx*=bounce;
			}
			if (ball.y+ball.radius>stage.stageHeight) {
				ball.y=stage.stageHeight-ball.radius;
				ball.vy*=bounce;
			} else if (ball.y-ball.radius<0) {
				ball.y=ball.radius;
				ball.vy*=bounce;
			}
		}
	}
}

原理图:

img_9f03f1e03d1a3821bc22a9d0fb846144.png

多物体基于距离的碰撞检测:

package {
	import flash.display.Sprite;
	import flash.events.Event;
	public class Bubbles2 extends Sprite {
		private var balls:Array;
		private var numBalls:Number=20;
		private var bounce:Number=-0.9;
		private var spring:Number=0.2;
		private var gravity:Number=1;
		public function Bubbles2() {
			init();
		}
		private function init():void {
			balls = new Array();
			for (var i:uint = 0; i < numBalls; i++) {
				var ball:Ball=new Ball(Math.random()*30+20,Math.random()*0xffffff);
				ball.x=Math.random()*stage.stageWidth;
				ball.y=Math.random()*stage.stageHeight;
				ball.vx=Math.random()*6-3;
				ball.vy=Math.random()*6-3;
				addChild(ball);
				balls.push(ball);
			}
			addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}
		private function onEnterFrame(event:Event):void {
			for (var i:uint = 0; i < numBalls - 1; i++) {
				var ball0:Ball=balls[i];
				for (var j:uint = i + 1; j < numBalls; j++) {
					var ball1:Ball=balls[j];					
					var dx:Number=ball1.x-ball0.x;
					var dy:Number=ball1.y-ball0.y;
					var dist:Number=Math.sqrt(dx*dx+dy*dy);
					var minDist:Number=ball0.radius+ball1.radius;
					if (dist<minDist) {
						/*
						var angle:Number=Math.atan2(dy,dx);
						var tx:Number=ball0.x+Math.cos(angle)*minDist;
						var ty:Number=ball0.y+Math.sin(angle)*minDist;
						*/						
						var tx:Number=ball0.x + (dx/dist)*minDist;
						var ty:Number=ball0.y + (dy/dist)*minDist;
						var ax:Number = (tx - ball1.x) * spring;
						var ay:Number = (ty - ball1.y) * spring;
						ball0.vx-=ax;
						ball0.vy-=ay;
						ball1.vx+=ax;
						ball1.vy+=ay;
					}
				}
			}
			for (i = 0; i < numBalls; i++) {
				var ball:Ball=balls[i];
				move(ball);
			}
		}
		private function move(ball:Ball):void {
			ball.vy+=gravity;
			ball.x+=ball.vx;
			ball.y+=ball.vy;
			if (ball.x+ball.radius>stage.stageWidth) {
				ball.x=stage.stageWidth-ball.radius;
				ball.vx*=bounce;
			} else if (ball.x - ball.radius < 0) {
				ball.x=ball.radius;
				ball.vx*=bounce;
			}
			if (ball.y+ball.radius>stage.stageHeight) {
				ball.y=stage.stageHeight-ball.radius;
				ball.vy*=bounce;
			} else if (ball.y - ball.radius < 0) {
				ball.y=ball.radius;
				ball.vy*=bounce;
			}
		}
	}
}

目录
相关文章
|
内存技术
Flash/Flex学习笔记(56):矩阵变换
先回顾一下Silvelright中的矩阵变换[转]WPF中的MatrixTransform,简单点讲:矩阵变换能改变对象的x,y坐标,x或y方向上的缩放,以及对象在x,y轴上的旋转(扭曲变形) 上面这个是WPF/Silverlight中的3*3变换矩阵,其中X,Y用于改变对象的坐标;M11,M22用于对象在x,y轴上的缩放;而M12,M21用于y轴,x轴上的扭曲。
846 0
|
内存技术
Flash/Flex学习笔记(51):3维旋转与透视变换(PerspectiveProjection)
Flash/Flex学习笔记(49):3D基础 里已经介绍了3D透视的基本原理,不过如果每次都要利用象该文中那样写一堆代码,估计很多人不喜欢,事实上AS3的DisplayObject类已经内置了z坐标、rotationX、rotationY、rotationZ属性,再加上PerspectiveProjection类用于处理透视转换,基本上可以满足大多数的3D要求。
1023 0
|
图形学 内存技术
Flash/Flex学习笔记(55):背面剔除与 3D 灯光
Animation in ActionScript3.0 这本书总算快学完了,今天继续:上一回Flash/Flex学习笔记(50):3D线条与填充 里,我们知道任何一个3D多面体上的某一个面,都可以分解为多个三角形的组合。
916 0
Flash/Flex学习笔记(54):迷你滚动条ScrollBar
先看最终效果: 整个swf最终不到4k,如果用系统的组件List来做的话,最终尺寸会接近30k ! (当然,核心代码是从网上收集到的:))   大致原理: 把要显示的对象上面加一层遮罩,然后根据滚动条的位置,上下移动显示对象。
931 0
|
索引 容器 内存技术
Flash/Flex学习笔记(52):使用TweenLite
TweenLite是第三方出品的专用于各种缓动动画的类库,其性能据说已经超过了Adobe官方的Tween. 从网上找到了一篇中文的说明文档:http://files.cnblogs.com/yjmyzz/tweenLite%e4%b8%ad%e6%96%87%e6%89%8b%e5%86%8c%e4%b8%8e%e5%8f%82%e6%95%b0%e8%af%b4%e6%98%8e.
905 0
|
Java 内存技术 Spring
Flash/Flex学习笔记(44):万有引力与粒子系统
万有引用公式: 其中G为万有引力常数   var numParticles:uint=50;//粒子总数 var G:Number=0.03;//万有引力常数 var particles:Array=new Array(numParticles); var bounce:Number=-0.
797 0
|
内存技术 C# 开发工具
Flash/Flex学习笔记(38):缓动动画
缓动 与 匀变速 看上去很类似,但其实有区别: 匀变速的公式为 V = V0 + at --速度v与时间t是线性(正比)关系,而且这种运动不需要确定目标点,速度可以按照这种规律一直变下去 而缓动指的是物体越接近目标时速度越慢,速度跟距离成反比关系,用公式描述为 V = k S  (0
813 0
|
vr&ar 内存技术
Flash/Flex学习笔记(42):坐标旋转
坐标旋转是个啥概念呢? 如上图,(蓝色)小球 绕某一中心点旋转a角度后,到达(红色)小球的位置,则红色小球相对中心点的坐标为: x1 = dx * cos(a) - dy * sin(a) y1 = dy * cos(a) + dx * sin(a)  这个就是坐标旋转公式,如果要反向旋转,则公式要修正一下,有二种方法:   1.
794 0
|
Java Spring 内存技术
Flash/Flex学习笔记(40):弹性运动续--弹簧
上一篇里演示的弹性运动加上摩擦力因素后,物体最终基本上都会比较准确的停在目标位置。但是我们回想一下现实世界中的弹簧,如果把弹簧的一头固定起来(即相当于目标点),而另一端栓一个球,把球拉开或压缩一定距离然后松手,事实上小球永远也不可能到达弹簧固定的那一端(因为弹簧即使压缩到最紧,也总有一定的长度) ...
785 0