在 iOS 9 中设置 UIView 的碰撞边界路径

Setting collision bounding path of a UIView in iOS 9

在 iOS 9 Apple 将 collisionBoundsType 引入 UIKit-Dynamics

设置 UIDynamicItemCollisionBoundsTypeRectangle 或设置 UIDynamicItemCollisionBoundsTypeEllipse 时没有问题。

下面的屏幕截图来自我正在制作的游戏,其中玩家的 collisionBoundsType 设置为矩形,球设置为椭圆形:

但是,当我将玩家的 collisionBoundsType 设置为路径时,我会出现奇怪的行为,如下所示:

视图显示得比应有的高,碰撞体在应有的位置右侧。

目前我collisionBoundingPath设置为:

- (UIBezierPath *)collisionBoundingPath
{
    maskPath = [[UIBezierPath alloc] init];
    [maskPath addArcWithCenter:CGPointMake(SLIME_SIZE, SLIME_SIZE) radius:SLIME_SIZE startAngle:0*M_PI endAngle:M_PI clockwise:NO];
    return maskPath;
}

此外,我的 drawRect 函数如下所示:

- (void) drawRect:(CGRect)rect
{
    if (!_color){
    [self returnDefualtColor];
    }
    if (!maskPath) maskPath = [[UIBezierPath alloc] init];
    [maskPath addArcWithCenter:CGPointMake(SLIME_SIZE, SLIME_SIZE) radius:SLIME_SIZE startAngle:0*M_PI endAngle:M_PI clockwise:NO];
    [_color setFill];
    [maskPath fill];
}

为什么会这样?如何设置碰撞体的路径与视图中的绘图一致?

此外,红色只是视图的背景(即 view.backgroundColor = [UIColor redColor];)。

我可以通过更改来回答这个问题:

- (UIBezierPath *)collisionBoundingPath
{
    maskPath = [[UIBezierPath alloc] init];
    [maskPath addArcWithCenter:CGPointMake(SLIME_SIZE, SLIME_SIZE) radius:SLIME_SIZE startAngle:0*M_PI endAngle:M_PI clockwise:NO];
    return maskPath;
}

至:

- (UIBezierPath *)collisionBoundingPath
{
    maskPath = [[UIBezierPath alloc] init];
    [maskPath addArcWithCenter:CGPointMake(SLIME_SIZE / 2, SLIME_SIZE / 2) radius:SLIME_SIZE startAngle:0*M_PI endAngle:M_PI clockwise:NO];
    return maskPath;
}

主要区别在于我通过将 x 和 y 值除以 2 修改了圆弧的中心。

调试物理是一回事。 iOS 用户可能不会考虑太多,因为他们通常使用 UIKit Dynamics 完成非常简单的事情。这有点遗憾,因为它是 iOS 最近版本的最佳方面之一,并提供了一种真正有趣的方式来打造引人入胜的用户体验。

那么...如何调试物理?

一种方法是在脑海中想象正在发生的事情,然后将其与正在发生的事情联系起来,找到想象与现实之间的不协调,然后通过混合消除过程(心理或现实)来解决问题反复试验和推演,直到确定并解决问题。

另一个是对所有创建和交互的内容进行视觉描述,提供足够的反馈,以更快地确定元素的性质和范围、它们的关系和 incidents/events,并通过文字视觉解决问题。

为此,自推出以来,已经创建了各种视觉调试器和物理模拟构建器。

不幸的是 iOS 没有这样一个基于屏幕的编辑器或 "scene editor" UIKit Dynamics,Sprite Kit 和 Scene Kit 中可用于这种可视化调试的东西充其量是基本的.

然而,存在于所有 UIKit 视图中的 CALayers,可以手动创建和绘制 CAShapeLayers 以准确表示任何和所有物理元素、它们的边界以及它们的锚点和关系。

CAShapeLayers 是 CGPaths 的 "container",可以有不同的轮廓和填充颜色,并且在单个 CAShapeLayer 中可以有多个 CGPath 元素。

而且,引用伟大的 Rob:

"If you add a CAShapeLayer as a layer to a view, you don't have to implement any drawing code yourself. Just add the CAShapeLayer and you're done. You can even later change the path, for example, and it will automatically redraw it for you. CAShapeLayer gets you out of the weeds of writing your own drawRect or drawLayer routines."

如果你有大量交互元素并且想要调试它们,CAShapeLayer 的性能问题可能会发挥作用,此时你可以使用 shouldRasterize 将每个元素转换为位图,并在点击时获得显着的性能提升由 CAShapeLayers 的 "dynamic" 功能创建的限制。

此外,为了表示约束和关节之类的东西,有一个简单的过程可以通过简单地设置属性在 CAShapeLayers 上创建虚线。这是设置 CAShapeLayer 属性的基础知识,以及使用数组创建 5-5-5 虚线轮廓的方法,该轮廓具有块状描边,宽度为 3,无填充。

CAShapeLayer *shapeLayer = [CAShapeLayer layer];
[shapeLayer setBounds:self.bounds];
[shapeLayer setPosition:self.center];
[shapeLayer setFillColor:[[UIColor clearColor] CGColor]];
[shapeLayer setStrokeColor:[[UIColor blackColor] CGColor]];
[shapeLayer setLineWidth:3.0f];
[shapeLayer setLineJoin:kCALineJoinRound];
[shapeLayer setLineDashPattern:
 [NSArray arrayWithObjects:[NSNumber numberWithInt:10],
  [NSNumber numberWithInt:5],nil]];

根据 UIDynamicItem here 的文档,以下关于路径坐标系的陈述似乎表明了错误所在:

The path object you create must represent a convex polygon with counter-clockwise or clockwise winding, and the path must not intersect itself. The (0, 0) point of the path must be located at the center point of the corresponding dynamic item. If the center point does not match the path’s origin, collision behaviors may not work as expected.

这里声明路径的(0,0)必须是中心点。

我认为你的圆弧路径的中心应该是 (0,0) 而不是 (SLIME_SIZE/2,SLIME_SIZE/2)。您是否将 UIView 框架的宽度和高度设置为 SLIME_SIZE 而不是 SLIME_SIZE*2

SLIME_SIZE好像真的定义了半径,所以框宽应该是SLIME_SIZE*2。如果设置为SLIME_SIZE,那就可以解释为什么你需要翻译SLIME_SIZE/2作为更正。