9RIA-ladeng6666 发表于 2018-2-6 14:56:10

【9RIA—ladeng6666】—【Box2D系列教程 新番】

转载:9RIA游戏开发者社区(天地会)
作者:ladeng6666(拉登大叔)
作者博客:http://www.ladeng6666.com/blog/


【Box2D系列教程-导航帖】—拉登大叔出品(总贴)


之前在学习emanueleferonato的Box2D刚体切割时,文中提到了Raycast函数,但是并没有对其Callback回调函数返回值进行讲解。看过API之后有了一个大概的了解,每个返回值的作用时这样的:
    0:立即停止Raycast的查找
    1:Raycast持续查找,直到达到线段的终点
    fraction:查找最近的碰撞刚体
前两个返回值都调试成功,正如API中所说的,callback返回值为0时,Raycast只找到了1个碰撞点。返回值为1时,Raycast把线段上所有的碰撞点都标示了出来。但是当返回值为fraction,Raycast并没有按照期望的那样找到最近的碰撞点,而是找出了多个,而且没有规律的点。在下面的示例中,Raycast找到的刚体被标示成了蓝色,同时交互点也用蓝色的圆圈表示。按下空格键,查看不同返回值的效果,你会发现返回值为fraction时,虽然查找结果大部分都时离鼠标最近的刚体,但是还是有一部分与红线发生重叠的刚体未被标示出来。点击图片查看Flash。


后来比对了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 = 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 = node.child1;
                                        stack = node.child2;
                                }
                        }
                }

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



下载:

页: [1]
查看完整版本: 【9RIA—ladeng6666】—【Box2D系列教程 新番】

感谢所有支持论坛的朋友:下面展示最新的5位赞助和充值的朋友……更多赞助和充值朋友的信息,请查看:永远的感谢名单

SGlW(66139)、 anghuo(841)、 whdsyes(255)、 longxia(60904)、 囫囵吞澡(58054)

下面展示总排行榜的前3名(T1-T3)和今年排行榜的前3名的朋友(C1-C3)……更多信息,请查看:总排行榜今年排行榜

T1. fhqu1462(969)、 T2. lwlpluto(14232)、 T3. 1367926921(962)  |  C1. anghuo(147)、 C2. fdisker(27945)、 C3. 囫囵吞澡(58054)