【问题标题】:Detecting collisions between rotated UIViews检测旋转的 UIView 之间的碰撞
【发布时间】:2014-02-13 15:20:32
【问题描述】:

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

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

现在,我希望能够判断另一个 UIView(称为 view)是否与 rectView 相交。我使用了这段代码:

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

但是,正如您可能知道的那样,这有一个问题。这是一个屏幕截图:

在这种情况下,会检测到碰撞,即使它们显然没有接触。我环顾四周,但似乎找不到真正的代码来检测这种碰撞。如何才能做到这一点?在这种情况下检测碰撞的工作代码将不胜感激!或者也许除了 UIView 之外还有更好的类可以使用?

【问题讨论】:

  • 我不知道答案,但原因可能是视图的框架不受应用于视图的任何变换的影响。
  • 好的@Jasarien,你知道我可以使用比 UIView 更好的类吗?
  • 如果你打算做很多类似的事情,我认为你应该使用一些 OpenGL 框架。如果它是随意的,你仍然可以做一些算术。但是这种方式已经在大多数游戏框架中完成了。
  • 如果您的应用程序适用于 iOS 7 及更高版本,请检查 UICollisionBehavior 类。
  • 如果您打算制作游戏,我建议您使用游戏框架,例如 Sprite Kit 或 Cocos 2d。虽然仍然可以使用 UIKit 制作游戏,但您会发现使用内置于框架中为您处理碰撞等事情的游戏框架要容易得多。

标签: ios objective-c uiview collision-detection


【解决方案1】:

当你旋转视图时,它的边界不会改变but its frame changes.

所以,对于我的背景颜色蓝色,
我设置的初始帧是

帧 = (30, 150, 150, 35);
bounds={{0, 0}, {150, 35}};

但旋转45度后,框架变为

帧 = (39.5926 102.093; 130.815 130.815);
bounds={{0, 0}, {150, 35}};

因为框架总是返回那个视图的最小的封闭矩形

因此,在您的情况下,即使看起来两个视图都没有相交,但它们的框架相交。

您可以使用分离轴测试来解决它。 如果你想学习它,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!
【解决方案2】:

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

只需将旋转的 blueView 的 4 个角转换为它们在 superview 中的位置,然后将这些点转换为它们在旋转的 blackView 中的位置,然后检查这些点是否在 blackView 的范围内。

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));

【讨论】:

  • 嗯,是不是有碰撞的可能,但是蓝色矩形的角都不在黑色矩形内?就像蓝色矩形呈 45 度角并且黑色矩形的角接触边一样。我想我们可能必须对黑色矩形的角进行相同的处理,检查它们是否也在蓝色矩形内?
  • @SamuelNoyes 上面的代码应该自动适用于两个矩形的所有角落,无论两个矩形的旋转如何。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-07
  • 2018-08-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多