iOS SpriteKit如何实现触摸穿透?让精灵节点不拦截下层触摸
在SpriteKit中实现触摸穿透(让顶层节点不拦截触摸)
核心思路
SpriteKit的触摸事件分发依赖hitTest(_:with:)方法,该方法会返回触摸点所在的最顶层节点。要让节点“忽略”触摸,关键是让它在hitTest时返回nil,这样系统会继续向下查找下层可响应触摸的节点。
解决方案1:重写节点的hitTest方法
给需要穿透触摸的节点(比如你的粒子发射器SKEmitterNode)创建子类,重写hitTest方法直接返回nil:
class PassthroughEmitterNode: SKEmitterNode { override func hitTest(_ point: CGPoint, with event: UIEvent?) -> SKNode? { // 返回nil,让触摸继续向下传递 return nil } }
使用时直接创建这个子类的实例作为粒子效果节点,添加到场景顶层即可。触摸到粒子区域时,系统会跳过该节点,去检测下层的可交互节点。
解决方案2:给现有节点动态设置穿透逻辑
如果不想创建子类,也可以通过扩展给节点添加自定义hitTest行为:
extension SKNode { func enableTouchPassthrough() { self.hitTestOverride = { _, _ in nil } } // 用关联对象存储自定义逻辑 private static let overrideKey = UnsafeRawPointer(bitPattern: "hitTestOverride".hashValue)! private var hitTestOverride: ((CGPoint, UIEvent?) -> SKNode?)? { get { objc_getAssociatedObject(self, SKNode.overrideKey) as? ((CGPoint, UIEvent?) -> SKNode?) } set { objc_setAssociatedObject(self, SKNode.overrideKey, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } } override open func hitTest(_ point: CGPoint, with event: UIEvent?) -> SKNode? { if let override = hitTestOverride { return override(point, event) } return super.hitTest(point, with: event) } }
给你的粒子节点调用以下代码即可生效:
yourEmitterNode.enableTouchPassthrough()
为什么isUserInteractionEnabled = false无效?
SpriteKit中,isUserInteractionEnabled仅控制节点是否触发自身的触摸事件(比如touchesBegan),但不会阻止它在hitTest中被选中。只要节点在触摸点范围内且是顶层节点,hitTest就会返回它,导致下层节点无法接收触摸事件。只有让节点在hitTest时返回nil,才能真正实现触摸穿透。
内容的提问来源于stack exchange,提问作者OMGPOP
相关产品推荐
相关产品推荐

