智慧 + 毅力 = 无所不能

正确性、健壮性、可靠性、效率、易用性、可读性、可复用性、兼容性、可移植性...
随笔 - 991, 文章 - 0, 评论 - 27, 阅读 - 341万

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

超高效的不规则物体碰撞检测

Posted on   Bill Yuan  阅读(3066)  评论(0编辑  收藏  举报

转自:http://blog.forfl.com/2012/03/super-efficient-collision-detection-of-irregular-objects/

复制代码
package ws.tink.display
{
    
    import flash.display.BitmapData;
    import flash.display.BlendMode;
    import flash.display.DisplayObject;
    import flash.display.Sprite;
    
    import flash.geom.ColorTransform;
    import flash.geom.Matrix;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    
    public class HitTest
    {
 
        public static function complexHitTestObject( target1:DisplayObject, target2:DisplayObject,  accurracy:Number = 1 ):Boolean
        {
            return complexIntersectionRectangle( target1, target2, accurracy ).width != 0;
        }
        
        public static function intersectionRectangle( target1:DisplayObject, target2:DisplayObject ):Rectangle
        {
            // If either of the items don't have a reference to stage, then they are not in a display list
            // or if a simple hitTestObject is false, they cannot be intersecting.
            if( !target1.root || !target2.root || !target1.hitTestObject( target2 ) ) return new Rectangle();
            
            // Get the bounds of each DisplayObject.
            var bounds1:Rectangle = target1.getBounds( target1.root );
            var bounds2:Rectangle = target2.getBounds( target2.root );
            
            // Determine test area boundaries.
            var intersection:Rectangle = new Rectangle();
            intersection.x   = Math.max( bounds1.x, bounds2.x );
            intersection.y    = Math.max( bounds1.y, bounds2.y );
            intersection.width      = Math.min( ( bounds1.x + bounds1.width ) - intersection.x, ( bounds2.x + bounds2.width ) - intersection.x );
            intersection.height = Math.min( ( bounds1.y + bounds1.height ) - intersection.y, ( bounds2.y + bounds2.height ) - intersection.y );
        
            return intersection;
        }
        
        public static function complexIntersectionRectangle( target1:DisplayObject, target2:DisplayObject, accurracy:Number = 1 ):Rectangle
        {             
            if( accurracy <= 0 ) throw new Error( "ArgumentError: Error #5001: Invalid value for accurracy", 5001 );
            
            // If a simple hitTestObject is false, they cannot be intersecting.
            if( !target1.hitTestObject( target2 ) ) return new Rectangle();
            
            var hitRectangle:Rectangle = intersectionRectangle( target1, target2 );
            // If their boundaries are no interesecting, they cannot be intersecting.
            if( hitRectangle.width * accurracy <1 || hitRectangle.height * accurracy <1 ) return new Rectangle();
            
            var bitmapData:BitmapData = new BitmapData( hitRectangle.width * accurracy, hitRectangle.height * accurracy, false, 0x000000 ); 
 
            // Draw the first target.
            bitmapData.draw( target1, HitTest.getDrawMatrix( target1, hitRectangle, accurracy ), new ColorTransform( 1, 1, 1, 1, 255, -255, -255, 255 ) );
            // Overlay the second target.
            bitmapData.draw( target2, HitTest.getDrawMatrix( target2, hitRectangle, accurracy ), new ColorTransform( 1, 1, 1, 1, 255, 255, 255, 255 ), BlendMode.DIFFERENCE );
            
            // Find the intersection.
            var intersection:Rectangle = bitmapData.getColorBoundsRect( 0xFFFFFFFF,0xFF00FFFF );
            
            bitmapData.dispose();
            
            // Alter width and positions to compensate for accurracy
            if( accurracy != 1 )
            {
                intersection.x /= accurracy;
                intersection.y /= accurracy;
                intersection.width /= accurracy;
                intersection.height /= accurracy;
            }
            
            intersection.x += hitRectangle.x;
            intersection.y += hitRectangle.y;
            
            return intersection;
        }
        
        
        protected static function getDrawMatrix( target:DisplayObject, hitRectangle:Rectangle, accurracy:Number ):Matrix
        {
            var localToGlobal:Point;;
            var matrix:Matrix;
            
            var rootConcatenatedMatrix:Matrix = target.root.transform.concatenatedMatrix;
            
            localToGlobal = target.localToGlobal( new Point( ) );
            matrix = target.transform.concatenatedMatrix;
            matrix.tx = localToGlobal.x - hitRectangle.x;
            matrix.ty = localToGlobal.y - hitRectangle.y;
            
            matrix.a = matrix.a / rootConcatenatedMatrix.a;
            matrix.d = matrix.d / rootConcatenatedMatrix.d;
            if( accurracy != 1 ) matrix.scale( accurracy, accurracy );
 
            return matrix;
        }
 
    }
 
} 
复制代码

相信已经有很多人使用过了这个像素级精确碰撞类,作者很NB思路很清晰。

但是在使用过程中,这个方法的实际效率并不是很高效,对于大一点的对象或者碰撞面积比较大的话来个1000次循环就已经超15秒了。这限制了如此优秀的算法在实际项目中的运用。在此由于论坛有讨论精确碰撞的贴,于是在原算法上加了一点小想法来优化检测效率,并且分享给大家。
首先原算法判断效率的关键就是两个对象重叠的矩形面积,面积越大效率越低。那么控制每次draw的面积就成了控制效率的关键。举个简单例子,绘制检测一次100*100的面积总比不上检测3到4次10*10的来的快。

原先的BitmapData之间精确判断顺序是这样的。
1。判断2容器矩形是否有重叠
2。有重叠则取两个容器的重叠部分draw到一个新BitmapData中并且把有色彩的部分全部纯色化,A容器放进去后B容器以一个滤镜模式再绘制进去。
3。判断该容器是否有A容器的颜色和B容器的颜色经过滤镜后产生的颜色。有就表示有碰撞,无则无碰撞。

优化的判断顺序如下:
1。判断2容器矩形是否有重叠
2。计算2容器的尺寸大小缩小到20*20需要缩小多少倍率,同时根据这个倍率重新计算重叠部分,也就是缩小后的重叠部分。
3。向一个20*20的BitmapData中绘制2个容器对象,经过一系列计算获取碰撞部分。
4。这时的重叠部分是缩小后图像的重叠部分,实际尺寸这个区域内可能并未碰撞,所以在压缩检测时有碰撞就需要进行再次检测。而检测时候把之前的倍率重新计算,计算原理是:由于获得碰撞区域一般都比原先矩形重叠部分小,那么用计算得来的这个区域代替原来的矩形重叠区域重新计算(跳到第2步)。
5。一直到缩小倍率降为1时仍然有碰撞,则说明2容器对象确实产生了碰撞。那么实际检测面积就是20*20*检测次数。(实际检测中就算测试对象尺寸6K*3K也很少会进行4次以上)

优化结果上对于超大尺寸可以提升5倍效率,小尺寸20*20以内打平,大于20*20的提升50%-500%不等。
不过里面有一些判断并非完善,希望大家在使用过程中多多改进。

(评论功能已被禁用)
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· [AI/GPT/综述] AI Agent的设计模式综述
点击右上角即可分享
微信分享提示