如何调整SKSpriteNode的可点击形状,使其仅响应纹理实体部分的点击手势?
解决方案
nodes(at:) 方法默认仅判断点是否落在节点的矩形边框内,不会识别纹理的透明区域,你需要额外增加像素透明度检测逻辑,实现只有点击到宝石实体区域才响应:
步骤1:给MyGem类增加透明区域检测方法
在你的MyGem类中新增如下方法,用来判断某个点是否落在纹理的非透明区域:
class MyGem : SKSpriteNode{ // 原有代码保持不变 // 新增检测方法 func isTouched(on point: CGPoint, alphaThreshold: CGFloat = 0.1) -> Bool { // 将触摸点转换为节点本地坐标系的点 let localPoint = self.convert(point, from: self.parent!) // 点不在节点边框内直接返回false guard self.bounds.contains(localPoint) else { return false } // 计算对应纹理上的像素坐标 let textureX = localPoint.x / self.size.width * self.texture!.size().width let textureY = (self.size.height - localPoint.y) / self.size.height * self.texture!.size().height let texturePoint = CGPoint(x: textureX, y: textureY) // 读取对应像素的alpha值 let alpha = self.texture!.pixelAlpha(at: texturePoint) return alpha > alphaThreshold } } // 给SKTexture增加读取像素alpha的扩展 extension SKTexture { func pixelAlpha(at point: CGPoint) -> CGFloat { let pixelData = self.cgImage().dataProvider!.data let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData) let bytesPerRow = 4 * Int(self.size().width) let pixelOffset = Int(point.y) * bytesPerRow + Int(point.x) * 4 return CGFloat(data[pixelOffset + 3]) / 255.0 } }
步骤2:修改点击手势的处理逻辑
在遍历节点时,增加透明度检测的判断:
@objc func tappedView(_ sender:UITapGestureRecognizer) { if sender.state == .ended{ var post = sender.location(in: sender.view) post = self.convertPoint(fromView: post) for node in self.nodes(at: post){ if let touchNode = node as? MyGem, touchNode.isTouched(on: post) { print("touched gem") highliteGem(theGem: touchNode, clearAll: false) return } } } }
注意事项
- alpha阈值可根据需求调整,0.1代表透明度超过10%的区域才会响应点击
- 如果场景中宝石数量超过100个,可提前生成纹理的alpha掩码缓存,避免每次点击都读取像素造成性能损耗
内容的提问来源于stack exchange,提问作者LuisC329
相关产品推荐
相关产品推荐

