iOS UIKit物理引擎(三)碰撞(UICollisionBehavior)

导语

这一篇文章我们将介绍UIKit物理引擎中有关碰撞的部分…

构造

1
2
UICollisionBehavior * collisionBehavior = 
[[UICollisionBehavior alloc] initWithItems:@[_dynamicItem1View]];

设置碰撞边界

1
2
3
UICollisionBehavior * collisionBehavior = [[UICollisionBehavior alloc] initWithItems:@[_dynamicItem1View]];
collisionBehavior.translatesReferenceBoundsIntoBoundary = YES;
[_animator addBehavior:collisionBehavior];


这是将重力向量设置为{1, 1}后,_dynamicItem1ViewReference View边界碰撞产生的效果。
translatesReferenceBoundsIntoBoundary属性表示是否以当前坐标系边界作为检测碰撞的边界。此时_dynamicItem1View运动到self.view的边界时就会发生碰撞。

碰撞的类型

1
2
3
4
5
6
7
8
9
10
@property (nonatomic, readwrite) UICollisionBehaviorMode collisionMode;

typedef NS_OPTIONS(NSUInteger, UICollisionBehaviorMode) {
// 元素之间的碰撞
    UICollisionBehaviorModeItems        = 1 << 0,
// 边界碰撞
    UICollisionBehaviorModeBoundaries   = 1 << 1,
// 碰撞所有
    UICollisionBehaviorModeEverything   = NSUIntegerMax
} NS_ENUM_AVAILABLE_IOS(7_0);
  • 元素之间的碰撞
    1
    2
    3
    4
    UICollisionBehavior * collisionBehavior = [[UICollisionBehavior alloc] initWithItems:@[_dynamicItem1View,_dynamicItem2View]];
    collisionBehavior.collisionMode = UICollisionBehaviorModeItems;
    collisionBehavior.translatesReferenceBoundsIntoBoundary = YES;
    [_animator addBehavior:collisionBehavior];


collisionMode属性为UICollisionBehaviorModeItems时,碰撞只发生在元素与元素之间,即使将translatesReferenceBoundsIntoBoundary属性设置为YES也不会检测边界碰撞。

  • 边界碰撞
    1
    2
    3
    4
    UICollisionBehavior * collisionBehavior = [[UICollisionBehavior alloc] initWithItems:@[_dynamicItem1View,_dynamicItem2View]];
    collisionBehavior.collisionMode = UICollisionBehaviorModeBoundaries;
    collisionBehavior.translatesReferenceBoundsIntoBoundary = YES;
    [_animator addBehavior:collisionBehavior];


collisionMode属性为UICollisionBehaviorModeBoundaries时,运动元素只检测是否与边界发生碰撞,而不会检测是否与其他元素进行碰撞。
这里我们将translatesReferenceBoundsIntoBoundary设置为YES,发现蓝球与Reference View边界发生了碰撞。
如果translatesReferenceBoundsIntoBoundaryNO又会怎样呢?

可以看出蓝球并没有与边界发生碰撞,这并不是因为collisionMode属性没有生效,而是因为当translatesReferenceBoundsIntoBoundaryNO时,就没有需要检测碰撞的边界了,所以蓝球因没有边界的阻挡会一直运动下去。
所以translatesReferenceBoundsIntoBoundary属性只负责碰撞边界的设置,并不会影响碰撞的类型。

  • 碰撞所有
    1
    2
    3
    4
    UICollisionBehavior * collisionBehavior = [[UICollisionBehavior alloc] initWithItems:@[_dynamicItem1View,_dynamicItem2View]];
    collisionBehavior.collisionMode = UICollisionBehaviorModeEverything;
    collisionBehavior.translatesReferenceBoundsIntoBoundary = YES;
    [_animator addBehavior:collisionBehavior];

碰撞边界内边距

1
2
3
4
5
UICollisionBehavior * collisionBehavior = [[UICollisionBehavior alloc] initWithItems:@[_dynamicItem1View,_dynamicItem2View]];
collisionBehavior.collisionMode = UICollisionBehaviorModeEverything;
collisionBehavior.translatesReferenceBoundsIntoBoundary = NO;
[collisionBehavior setTranslatesReferenceBoundsIntoBoundaryWithInsets:UIEdgeInsetsMake(50, 50, 50, 50)];
[_animator addBehavior:collisionBehavior];

setTranslatesReferenceBoundsIntoBoundaryWithInsets:函数是用来设置碰撞Reference View边界的内边距的。

通过效果可以看出,碰撞边界的上下左右分别向内收缩了50像素的距离。这里的translatesReferenceBoundsIntoBoundary属性被设置为NO,但是碰撞边界依然是有的,也就是说setTranslatesReferenceBoundsIntoBoundaryWithInsets:函数和translatesReferenceBoundsIntoBoundary属性设置的边界是相互独立的,两者之间没有联系。

自定义碰撞边界

  • 通过添加贝塞尔曲线,添加碰撞边界
    1
    -(void)addBoundaryWithIdentifier:(id <NSCopying>)identifier forPath:(UIBezierPath *)bezierPath;
1
2
3
4
5
6
7
8
9
// 创建椭圆形路径对象
UIBezierPath * path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(50, 50, 200, 500)];
path.lineWidth     = 1.f;
path.lineCapStyle  = kCGLineCapRound;
path.lineJoinStyle = kCGLineCapRound;
UICollisionBehavior * collisionBehavior = [[UICollisionBehavior alloc] initWithItems:@[_dynamicItem1View]];
collisionBehavior.collisionMode = UICollisionBehaviorModeEverything;
[collisionBehavior addBoundaryWithIdentifier:@"path1" forPath:path];
[_animator addBehavior:collisionBehavior];


有关UIBezierPath请看:UIBezierPath介绍

  • 通过添加由两点组成的线段,添加碰撞边界
    1
    -(void)addBoundaryWithIdentifier:(id <NSCopying>)identifier fromPoint:(CGPoint)p1 toPoint:(CGPoint)p2;
1
2
3
4
UICollisionBehavior * collisionBehavior = [[UICollisionBehavior alloc] initWithItems:@[_dynamicItem1View]];
collisionBehavior.collisionMode = UICollisionBehaviorModeEverything;
[collisionBehavior addBoundaryWithIdentifier:@"path2" fromPoint:CGPointMake(350, 100) toPoint:CGPointMake(150, 500)];
[_animator addBehavior:collisionBehavior];

效果中的红色边界线实际是看不到的,这里是为了效果格外添加上的。

  • 自定义边界其他相关方法
    1
    2
    3
    4
    5
    6
    7
    8
    // 根据获取指定已命名的碰撞边界的贝塞尔曲线
    -(nullable UIBezierPath *)boundaryWithIdentifier:(id <NSCopying>)identifier;
    // 移除指定已命名的碰撞边界
    -(void)removeBoundaryWithIdentifier:(id <NSCopying>)identifier;
    // 获得所有命名
    @property (nullable, nonatomic, readonly, copy) NSArray<id <NSCopying>> *boundaryIdentifiers;
    // 移除所有添加的碰撞边界
    -(void)removeAllBoundaries;

代理方法

1
2
3
4
5
6
7
8
9
10
11
12
@protocol UICollisionBehaviorDelegate <NSObject>
@optional
// 当一个两个动态元素之间发生碰撞时调用
- (void)collisionBehavior:(UICollisionBehavior *)behavior beganContactForItem:(id <UIDynamicItem>)item1 withItem:(id <UIDynamicItem>)item2 atPoint:(CGPoint)p;
// 当一个两个动态元素之间碰撞结束时调用
- (void)collisionBehavior:(UICollisionBehavior *)behavior endedContactForItem:(id <UIDynamicItem>)item1 withItem:(id <UIDynamicItem>)item2;

// 当一个动态元素与边界发生碰撞时调用
- (void)collisionBehavior:(UICollisionBehavior*)behavior beganContactForItem:(id <UIDynamicItem>)item withBoundaryIdentifier:(nullable id <NSCopying>)identifier atPoint:(CGPoint)p;
// 当一个动态元素与边界碰撞结束时调用
- (void)collisionBehavior:(UICollisionBehavior*)behavior endedContactForItem:(id <UIDynamicItem>)item withBoundaryIdentifier:(nullable id <NSCopying>)identifier;
@end

版权声明:出自Jerry LMJ的原创作品,未经作者允许不得转载。

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器