关于Flash版Box2D Raycast返回fraction计算错误

之前在学习emanueleferonato的Box2D刚体切割时,文中提到了Raycast函数,但是并没有对其Callback回调函数返回值进行讲解。看过API之后有了一个大概的了解,每个返回值的作用时这样的:

  • 0:立即停止Raycast的查找
  • 1:Raycast持续查找,直到达到线段的终点
  • fraction:查找最近的碰撞刚体

前两个返回值都调试成功,正如API中所说的,callback返回值为0时,Raycast只找到了1个碰撞点。返回值为1时,Raycast把线段上所有的碰撞点都标示了出来。但是当返回值为fraction,Raycast并没有按照期望的那样找到最近的碰撞点,而是找出了多个,而且没有规律的点。在下面的示例中,Raycast找到的刚体被标示成了蓝色,同时交互点也用蓝色的圆圈表示。按下空格键,查看不同返回值的效果,你会发现返回值为fraction时,虽然查找结果大部分都时离鼠标最近的刚体,但是还是有一部分与红线发生重叠的刚体未被标示出来。点击图片查看Flash。
DemoRaycast_original 后来比对了C++版Box2D的源代码之后,发现了Flash版Box2D的代码有些问题,具体在collision.b2DynamicTree.as中,按照C++版源码修改了它的Raycast()函数的部分代码如下:

		public function RayCast(callback:Function, input:b2RayCastInput):void
		{
			if (m_root == null)
				return;
				
			var p1:b2Vec2 = input.p1;
			var p2:b2Vec2 = input.p2;
			var r:b2Vec2 = b2Math.SubtractVV(p1, p2);
			//b2Settings.b2Assert(r.LengthSquared() > 0.0);
			r.Normalize();
			
			// v is perpendicular to the segment
			var v:b2Vec2 = b2Math.CrossFV(1.0, r);
			var abs_v:b2Vec2 = b2Math.AbsV(v);
			
			var maxFraction:Number = input.maxFraction;
			
			// Build a bounding box for the segment
			var segmentAABB:b2AABB = new b2AABB();
			var tX:Number;
			var tY:Number;
			{
				tX = p1.x + maxFraction * (p2.x - p1.x);
				tY = p1.y + maxFraction * (p2.y - p1.y);
				segmentAABB.lowerBound.x = Math.min(p1.x, tX);
				segmentAABB.lowerBound.y = Math.min(p1.y, tY);
				segmentAABB.upperBound.x = Math.max(p1.x, tX);
				segmentAABB.upperBound.y = Math.max(p1.y, tY);
			}
			
			var stack:Vector.<b2DynamicTreeNode> = new Vector.<b2DynamicTreeNode>();
			
			var count:int = 0;
			stack[count++] = m_root;
			
			while (count > 0)
			{
				var node:b2DynamicTreeNode = stack[--count];
				
				if (node.aabb.TestOverlap(segmentAABB) == false)
				{
					continue;
				}
				
				// Separating axis for segment (Gino, p80)
				// |dot(v, p1 - c)| > dot(|v|,h)
				
				var c:b2Vec2 = node.aabb.GetCenter();
				var h:b2Vec2 = node.aabb.GetExtents();
				var separation:Number = Math.abs(v.x * (p1.x - c.x) + v.y * (p1.y - c.y))
										- abs_v.x * h.x - abs_v.y * h.y;
				if (separation > 0.0)
					continue;
				
				if (node.IsLeaf())
				{
					var subInput:b2RayCastInput = new b2RayCastInput();
					subInput.p1 = input.p1;
					subInput.p2 = input.p2;
		//================================
		// udpate by ladeng6666 2014-08-01
		subInput.maxFraction = maxFraction;

		var value:Number = callback(subInput, node);
		
		if (value == 0.0)
			return;
		//Update the segment bounding box
		if(value > 0){
			maxFraction = value;
		//================================
						tX = p1.x + maxFraction * (p2.x - p1.x);
						tY = p1.y + maxFraction * (p2.y - p1.y);
						segmentAABB.lowerBound.x = Math.min(p1.x, tX);
						segmentAABB.lowerBound.y = Math.min(p1.y, tY);
						segmentAABB.upperBound.x = Math.max(p1.x, tX);
						segmentAABB.upperBound.y = Math.max(p1.y, tY);
					}
				}
				else
				{
					// No stack limit, so no assert
					stack[count++] = node.child1;
					stack[count++] = node.child2;
				}
			}
		}

修改之后再调试,当Raycast的Callback函数返回值为fraction,距离最近的刚体和碰撞点都被准确的标示出来了。在下面的示例中,按下空格键,查看修改后的效果。点击图片查看Flash。
DemoRaycast

点击下载源文件

联系作者

公众号:拉小登 | 微博:拉登Dony | B站:拉小登Excel

2 Replies to “关于Flash版Box2D Raycast返回fraction计算错误”

回复 羔羊引擎

您的电子邮箱地址不会被公开。 必填项已用 * 标注