检测旋转UIView之间的碰撞

3

我有两个UIView,其中一个每0.01秒旋转一次,使用以下代码:

    self.rectView.transform = CGAffineTransformRotate(self.rectView.transform, .05);

现在,我希望能够判断另一个名为view的UIView是否与rectView相交。我使用了以下代码:

if(CGRectIntersectsRect(self.rectView.frame, view.frame)) {
    //Intersection
}

然而,您可能已经知道,这会导致问题。以下是屏幕截图:enter image description here

在这种情况下,即使明显没有接触,也会检测到碰撞。我已经查找了一些资料,但似乎找不到真正的代码来检测此碰撞。如何完成?非常感谢提供有关在此情况下检测碰撞的有效代码!或者是否有其他比UIView更好的类可用于此任务?


我不知道答案,但原因可能是由于视图的框架不受应用于视图的任何变换的影响。 - Jasarien
好的 @Jasarien,你知道我可以使用哪个比UIView更好的类吗? - Samuel Noyes
我认为如果你计划做很多类似的事情,你应该使用一些OpenGL框架。如果只是随意的话,你仍然可以进行一些算术运算。但这种类型已经在大多数游戏框架中完成了。 - Tancrede Chazallet
如果您的应用程序是针对iOS 7及以上版本,请检查UICollisionBehavior类。 - Emmanuel
如果你打算制作游戏,我建议使用游戏框架,比如Sprite Kit或Cocos 2d。虽然仍然可以使用UIKit制作游戏,但是使用游戏框架会更容易处理像碰撞这样的事情,因为它们已经内置在框架中了。 - Jasarien
显示剩余2条评论
2个回答

2
当您旋转视图时,其边界不会改变,但其框架会改变。因此,对于我的背景颜色为蓝色的视图,我设置的初始框架是frame = (30, 150, 150, 35),bounds={{0, 0}, {150, 35}},但旋转45度后,框架变为frame = (39.5926 102.093; 130.815 130.815),bounds={{0, 0}, {150, 35}}。

screenshot of running app showing frame of blue view with black border

因为框架始终返回该视图的最小包围矩形,所以在您的情况下,即使看起来这两个视图不相交,它们的框架也会相交。要解决这个问题,您可以使用分离轴测试。如果您想了解更多信息,请单击此链接:link here
我试着解决这个问题,并最终得到了解决方案。如果您想检查,请查看下面的代码。将以下代码复制并粘贴到空项目中以进行检查。
在.m文件中:
@implementation ViewController{
    UIView *nonRotatedView;
    UIView *rotatedView;
}

- (void)viewDidLoad
{
    [super viewDidLoad];
    nonRotatedView =[[UIView alloc] initWithFrame:CGRectMake(120, 80, 150, 40)];
    nonRotatedView.backgroundColor =[UIColor blackColor];
    [self.view addSubview:nonRotatedView];

    rotatedView =[[UIView alloc] initWithFrame:CGRectMake(30, 150, 150, 35)];
    rotatedView.backgroundColor =[UIColor blueColor];
    [self.view addSubview:rotatedView];
    CGAffineTransform t=CGAffineTransformMakeRotation(M_PI_4);
    rotatedView.transform=t;

    CAShapeLayer *layer =[CAShapeLayer layer];
    [layer setFrame:rotatedView.frame];
    [self.view.layer addSublayer:layer];
    [layer setBorderColor:[UIColor blackColor].CGColor];
    [layer setBorderWidth:1];

    CGPoint p=CGPointMake(rotatedView.bounds.size.width/2, rotatedView.bounds.size.height/2);

    p.x = -p.x;p.y=-p.y;
    CGPoint tL =CGPointApplyAffineTransform(p, t);
    tL.x +=rotatedView.center.x;
    tL.y +=rotatedView.center.y;

    p.x = -p.x;
    CGPoint tR =CGPointApplyAffineTransform(p, t);
    tR.x +=rotatedView.center.x;
    tR.y +=rotatedView.center.y;

    p.y=-p.y;
    CGPoint bR =CGPointApplyAffineTransform(p, t);
    bR.x +=rotatedView.center.x;
    bR.y +=rotatedView.center.y;

    p.x = -p.x;
    CGPoint bL =CGPointApplyAffineTransform(p, t);
    bL.x +=rotatedView.center.x;
    bL.y +=rotatedView.center.y;


    //check for edges of nonRotated Rect's edges
    BOOL contains=YES;
    CGFloat value=nonRotatedView.frame.origin.x;
    if(tL.x<value && tR.x<value && bR.x<value && bL.x<value)
        contains=NO;
    value=nonRotatedView.frame.origin.y;
    if(tL.y<value && tR.y<value && bR.y<value && bL.y<value)
        contains=NO;
    value=nonRotatedView.frame.origin.x+nonRotatedView.frame.size.width;
    if(tL.x>value && tR.x>value && bR.x>value && bL.x>value)
        contains=NO;
    value=nonRotatedView.frame.origin.y+nonRotatedView.frame.size.height;
    if(tL.y>value && tR.y>value && bR.y>value && bL.y>value)
        contains=NO;

    if(contains==NO){
        NSLog(@"no intersection 1");
        return;
    }
    //check for roatedView's edges
    CGPoint rotatedVertexArray[]={tL,tR,bR,bL,tL,tR};

    CGPoint nonRotatedVertexArray[4];
    nonRotatedVertexArray[0]=CGPointMake(nonRotatedView.frame.origin.x,nonRotatedView.frame.origin.y);
    nonRotatedVertexArray[1]=CGPointMake(nonRotatedView.frame.origin.x+nonRotatedView.frame.size.width,nonRotatedView.frame.origin.y);
    nonRotatedVertexArray[2]=CGPointMake(nonRotatedView.frame.origin.x+nonRotatedView.frame.size.width,nonRotatedView.frame.origin.y+nonRotatedView.frame.size.height);
    nonRotatedVertexArray[3]=CGPointMake(nonRotatedView.frame.origin.x,nonRotatedView.frame.origin.y+nonRotatedView.frame.size.height);

    NSInteger i,j;
    for (i=0; i<4; i++) {
        CGPoint first=rotatedVertexArray[i];
        CGPoint second=rotatedVertexArray[i+1];
        CGPoint third=rotatedVertexArray[i+2];
        CGPoint mainVector =CGPointMake(second.x-first.x, second.y-first.y);
        CGPoint selfVector =CGPointMake(third.x-first.x, third.y-first.y);
        BOOL sign;
        sign=[self crossProductOf:mainVector withPoint:selfVector];
        for (j=0; j<4; j++) {
            CGPoint otherPoint=nonRotatedVertexArray[j];
            CGPoint otherVector = CGPointMake(otherPoint.x-first.x, otherPoint.y-first.y);
            BOOL checkSign=[self crossProductOf:mainVector withPoint:otherVector];
            if(checkSign==sign)
                break;
            else if (j==3)
                contains=NO;
        }
        if(contains==NO){
            NSLog(@"no intersection 2");
            return;
        }
    }
    NSLog(@"intersection");
}


-(BOOL)crossProductOf:(CGPoint)point1 withPoint:(CGPoint)point2{
    if((point1.x*point2.y-point1.y*point2.x)>=0)
         return YES;
    else
        return NO;
}

希望这有所帮助。

哇,比我可能想象的要好得多。如果可以的话,我会给这个点赞一百万次。非常感谢 @santhu! - Samuel Noyes

0

这可以比建议的更有效和容易地完成...如果需要,黑色和蓝色视图都可以旋转。

只需将旋转后的蓝色视图的4个角转换为其在superview中的位置,然后将这些点转换为它们在旋转后的黑色视图中的位置,然后检查这些点是否在黑色视图的边界内。

UIView *superview = blueView.superview;//Assuming blueView.superview and blackView.superview are the same...

CGPoint blueView_topLeft_inSuperview = [blueView convertPoint:CGPointMake(0, 0) toView:superview];
CGPoint blueView_topRight_inSuperview = [blueView convertPoint:CGPointMake(blueView.bounds.size.width, 0) toView:superview];
CGPoint blueView_bottomLeft_inSuperview = [blueView convertPoint:CGPointMake(0, blueView.bounds.size.height) toView:superview];
CGPoint blueView_bottomRight_inSuperview = [blueView convertPoint:CGPointMake(blueView.bounds.size.width, blueView.bounds.size.height) toView:superview];

CGPoint blueView_topLeft_inBlackView = [superview convertPoint:blueView_topLeft_inSuperview toView:blackView];
CGPoint blueView_topRight_inBlackView = [superview convertPoint:blueView_topRight_inSuperview toView:blackView];
CGPoint blueView_bottomLeft_inBlackView = [superview convertPoint:blueView_bottomLeft_inSuperview toView:blackView];
CGPoint blueView_bottomRight_inBlackView = [superview convertPoint:blueView_bottomRight_inSuperview toView:blackView];

BOOL collision = (CGRectContainsPoint(blackView.bounds, blueView_topLeft_inBlackView) ||
                  CGRectContainsPoint(blackView.bounds, blueView_topRight_inBlackView) ||
                  CGRectContainsPoint(blackView.bounds, blueView_bottomLeft_inBlackView) ||
                  CGRectContainsPoint(blackView.bounds, blueView_bottomRight_inBlackView));

1
嗯,如果蓝色矩形的四个角都不在黑色矩形内,是否存在碰撞的可能性呢?比如说,如果蓝色矩形以45度角倾斜,并且黑色矩形的一个角刚好与其相接触。我想我们可能需要对黑色矩形的四个角也进行同样的处理,检查它们是否在蓝色矩形内部。 - Samuel Noyes
@SamuelNoyes 上述代码应该自动适用于两个矩形的所有角落,无论它们是否旋转。 - Albert Renshaw

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接