检测SKSpriteNode上的触摸,其中包含运行动画的奇怪形状的图像

时间:2016-10-20 23:48:20

标签: swift sprite-kit skspritenode skcropnode

将SpriteKit用于iOS 9.0应用程序/游戏(并且没有物理主体)。

我在场景中有一些SpriteNodes。 SpriteNodes具有形状奇特的图像,并通过多个帧进行动画制作。

检测SpriteNode图像内容上的触摸的最佳方法是什么,而不是整个图像矩形的透明区域。

我看到很多关于使用SKCropNode / MaskImage的帖子。在我的场景中,我为动画的每个SpriteNode都有多个图像/帧。

请就方法提出建议或指出我正确的方向。感谢。

2 个答案:

答案 0 :(得分:2)

根据上述评论的其他信息:

在16帧中只有16个形状,其中一种更有效的方法是绘制粗略接近每个帧的角色轮廓的原始形状,并将它们转换为CGPaths。然后可以根据触摸时当前正在显示的帧进行测试时,可以对每帧进行换入或换出(或更好)适当的拔出。

CGPaths是非常轻量级的结构,因此任何一种方法都不应该出现任何性能问题。

CGPathContainsPoint是此测试的旧名称,现在是Swift 3及其后续版本的现代化API:

Troubles using CGPathContainsPoint SWIFT

有一个名为PaintCode的应用程序,价格约为100美元,可将矢量转换为CGPath,以及其他内容,因此您可以使用它来导入形状。您可以将矢量复制/粘贴到其中,我建议,因为您可能希望在友好的绘图程序中绘制。 PaintCode没有世界上最好的绘画经验。

此外,这是一个免费的在线工具,用于从纹理创建多边形路径。可能比使用绘图应用程序和PaintCode更痛苦,但它是FREE!

替代方案:纹理物理体和触摸接触

您可以通过this page上的文档自动从纹理创建物理体形状:

let texturedSpaceShip = SKSpriteNode(texture: spaceShipTexture)
texturedSpaceShip.physicsBody = SKPhysicsBody(texture: spaceShipTexture,
                                              size: CGSize(width: circularSpaceShip.size.width,
                                                           height: circularSpaceShip.size.height))

有报道称确定一个点是否在给定的物理体内存在问题,因此最好创建一个小的物理对象,将其置于触摸所在的位置,然后确定它是否与物理接触适合当前游戏实体当前帧的体形。

警告: 对于这些自动创建的物理形状,结果CGPath的复杂程度并不明确(也无法控制)。

答案 1 :(得分:0)

我个人只会检查当前纹理的像素,看看是否透明。您可以这样做以获取像素数据:

(注意这段代码是假设你开始的,如果你能解决这个问题,我会在稍后为你制作一个真实的例子。)

(另请注意,如果你缩放精灵,你需要处理将触摸位置转换为纹理)

let image = sprite.texture.cgImage()
if let dataProvider = image.dataProvider, let data = dataProvider.data
{

  let screenScale = UIScreen.main.scale //let's handle retina graphics (may need work)
  //touchedLocation is the location on the sprite, if the anchor was bottom left (it may be top left, will have to verify) 
  //So if I touch the bottom left corner of the sprite, I should get (0,0)
  let x = touchedLocation.x * screenScale
  let y = touchedLocation.y * screenScale
  let width = sprite.texture.size().width * screenScale
  let bpp = 4 // 4 bytes per pixel
  let alphaChannel = 3 //Alpha channel is usually the highest byte
  let index = bpp * (y * width + x) + alphaChannel
  let byte = data.withUnsafeBytes({[UInt8](UnsafeBufferPointer(start:$0 + index,count:1))})
  print(Alpha: \(byte))
}
else
{
  //we have no data
}