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学习笔记(11):如何检测摄像头是否被占用
原理:摄像头激活后,持续检测与之关联的Video对象是否在播放(即每秒帧数) btnCheck.addEventListener(MouseEvent.CLICK,btnCheckClick); var cam:Camera; var video:Video; var intervalI...
934 0
|
5月前
|
前端开发 算法 Java
(CSS)使用Flex布局,帮助你快速了解各种基本的Flex布局属性以及帮你让元素快速达到布局中的指定位置!
(CSS)使用Flex布局,帮助你快速了解各种基本的Flex布局属性以及帮你让元素快速达到布局中的指定位置!
184 1
|
5月前
|
前端开发 算法 Java
【CSS】前端三大件之一,如何学好?从基本用法开始吧!(六):全方面分析css的Flex布局,从纵、横两个坐标开始进行居中、两端等元素分布模式;刨析元素间隔、排序模式等
Flex 布局 布局的传统解决方案,基于盒状模型,依赖 display 属性 + position属性 + float属性。它对于那些特殊布局非常不方便,比如,垂直居中就不容易实现。 2009年,W3C 提出了一种新的方案----Flex 布局,可以简便、完整、响应式地实现各种页面布局。目前,它已经得到了所有浏览器的支持,这意味着,现在就能很安全地使用这项功能。 一、Flex 布局是什么? Flex 是 Flexible Box 的缩写,意为"弹性布局",用来为盒状模型提供最大的灵活性。
399 1
|
9月前
|
设计模式 容器
13.HarmonyOS流式卡片列表实现指南:Flex多行布局详解
在现代移动应用开发中,流式卡片列表是一种常见且实用的UI设计模式。它能够自适应屏幕宽度,在有限空间内高效展示多个内容项。本教程将详细讲解如何使用HarmonyOS的ArkUI框架中的Flex组件实现一个灵活的流式卡片列表,重点关注多行布局与对齐策略的应用。
306 2
|
9月前
|
开发者 UED 容器
07.精通HarmonyOS Flex对齐:从基础到高级布局技巧(上)
在HarmonyOS Next的ArkUI框架中,Flex容器提供了强大而灵活的对齐系统,使开发者能够精确控制子元素在容器中的排列方式。掌握这些对齐技术,是构建专业级用户界面的关键。
267 0
|
9月前
|
UED 容器
5.HarmonyOS Next开发宝典:掌握Flex布局的艺术
Flex布局(弹性布局)是HarmonyOS Next中最强大的布局方式之一,它提供了一种更加高效、灵活的方式来对容器中的子元素进行排列、对齐和分配空间。无论是简单的居中显示,还是复杂的自适应界面,Flex布局都能轻松应对。
276 0
|
开发者 容器
鸿蒙开发:弹性布局Flex
在实际的开发中,需要掌握主轴与交叉轴的关系、换行规则及子元素属性,同时注意性能与兼容性问题,还有一点,Flex组件在渲染时存在二次布局过程,因此在对性能有严格要求的场景下建议使用Column、Row代替。
322 10
鸿蒙开发:弹性布局Flex
|
9月前
|
UED 容器
10.HarmonyOS Next布局进阶:嵌套Flex容器与空间分配策略
在HarmonyOS Next的ArkUI框架中,Flex布局是构建用户界面的核心技术之一。通过嵌套使用Flex容器,我们可以创建复杂而灵活的界面结构,满足各种应用场景的需求。本教程将深入探讨如何在HarmonyOS Next中使用嵌套Flex容器实现复杂布局,以及如何合理分配和控制空间。
274 0
|
前端开发 容器
flex布局
【10月更文挑战第7天】
324 87
|
前端开发 UED 容器
使用 Flex 布局实现垂直居中效果
【10月更文挑战第7天】
1565 57

热门文章

最新文章