You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 09:13:24