实现可点击形状的最佳方法?
Most optimal method of implementing tappable shapes?
我正在构建一个应用程序,它提供类似于 photoshop 或 illustrator(目前使用 SpriteKit
)的可编辑 canvas。正如您想象的那样,我在显示大量节点 (1000+) 时遇到了一些性能问题。
目前,我有一个 SKScene
上面有 SKShapeNodes
。它们目前没有任何图像,只是填充了一种颜色。它们的形状路径 (CGPath
s) 从圆形到贝塞尔曲线路径不等。每个形状当前都有一个 SKPhysicsBody
,其路径与用于检测点击的渲染形状相同。
性能问题可以描述为:
- 添加 1000 个节点(圆圈)时速度较慢,每个节点使用大约 0.1mb 的内存
- 移动 1000 个节点(圆圈)时速度较慢
- 从 1000 个节点(圆圈)生成纹理时速度较慢
禁用 PhysicsBodies 不会 显着提高性能,但会提高 CPU 负载(从恒定的 60% 跳到 1% 左右)
大多数用户不会使用 1000 个节点,但我想实施最佳解决方案。
我想要的是两个有两层:
- 一个 render 层,我希望能够在其上渲染
CGPath
s 带有笔触和填充(最好选择笔触端盖样式等东西)
- 一个 interaction 层,我希望能够在该层上检测
CGPath
s 内的点击并用颜色描边 CGPath 以指示突出显示。
我怎样才能完成这个或类似的解决方案来提高渲染 1000 个圆圈的速度?
不要使用 SKShapeNode,每个形状节点需要 1 个绘制调用。您可以创建一个形状,然后 "cast" 将其添加到 SpriteNode,然后再将其添加到场景中:
func shapeToSprite(_ shape: SKShapeNode) -> SKSpriteNode {
let sprite = SKSpriteNode(texture: SKView().texture(from: shape))
sprite.physicsBody = shape.physicsBody // Or create a new PB from alpha mask (may be slower, IDK)
shape.physicsBody = nil
return sprite
}
override func didMove(to view: SKView) {
let shape = SKShapeNode(circleOfRadius: 60)
addChild(shapeToSprite(shape))
}
如果您需要以一种无法通过缩放或 SKWarpGeometry 实现的方式不断编辑形状的大小/形状,这会很糟糕
要检测这样的点击,您只需要 1,为 SpriteNode 设置一个名称,然后在 touchesBegan 中查找它,或者 2,将 SpriteNode 子类化并覆盖它的 touchesBegan。
此外,如果您只需要一个静态图像,您可以将数千个节点位块传输到 1 个纹理上。
Sprite Kit: A Lot of sprites (1000+) with Bit Blitting
因此,如果您不需要每个节点始终可触摸,您可以在拥有和不拥有 physicsBody 之间交替,或者将层从 bitblitted 切换为活动。
我正在构建一个应用程序,它提供类似于 photoshop 或 illustrator(目前使用 SpriteKit
)的可编辑 canvas。正如您想象的那样,我在显示大量节点 (1000+) 时遇到了一些性能问题。
目前,我有一个 SKScene
上面有 SKShapeNodes
。它们目前没有任何图像,只是填充了一种颜色。它们的形状路径 (CGPath
s) 从圆形到贝塞尔曲线路径不等。每个形状当前都有一个 SKPhysicsBody
,其路径与用于检测点击的渲染形状相同。
性能问题可以描述为:
- 添加 1000 个节点(圆圈)时速度较慢,每个节点使用大约 0.1mb 的内存
- 移动 1000 个节点(圆圈)时速度较慢
- 从 1000 个节点(圆圈)生成纹理时速度较慢
禁用 PhysicsBodies 不会 显着提高性能,但会提高 CPU 负载(从恒定的 60% 跳到 1% 左右)
大多数用户不会使用 1000 个节点,但我想实施最佳解决方案。
我想要的是两个有两层:
- 一个 render 层,我希望能够在其上渲染
CGPath
s 带有笔触和填充(最好选择笔触端盖样式等东西) - 一个 interaction 层,我希望能够在该层上检测
CGPath
s 内的点击并用颜色描边 CGPath 以指示突出显示。
我怎样才能完成这个或类似的解决方案来提高渲染 1000 个圆圈的速度?
不要使用 SKShapeNode,每个形状节点需要 1 个绘制调用。您可以创建一个形状,然后 "cast" 将其添加到 SpriteNode,然后再将其添加到场景中:
func shapeToSprite(_ shape: SKShapeNode) -> SKSpriteNode {
let sprite = SKSpriteNode(texture: SKView().texture(from: shape))
sprite.physicsBody = shape.physicsBody // Or create a new PB from alpha mask (may be slower, IDK)
shape.physicsBody = nil
return sprite
}
override func didMove(to view: SKView) {
let shape = SKShapeNode(circleOfRadius: 60)
addChild(shapeToSprite(shape))
}
如果您需要以一种无法通过缩放或 SKWarpGeometry 实现的方式不断编辑形状的大小/形状,这会很糟糕
要检测这样的点击,您只需要 1,为 SpriteNode 设置一个名称,然后在 touchesBegan 中查找它,或者 2,将 SpriteNode 子类化并覆盖它的 touchesBegan。
此外,如果您只需要一个静态图像,您可以将数千个节点位块传输到 1 个纹理上。 Sprite Kit: A Lot of sprites (1000+) with Bit Blitting
因此,如果您不需要每个节点始终可触摸,您可以在拥有和不拥有 physicsBody 之间交替,或者将层从 bitblitted 切换为活动。