2

我的场景中有几个精灵节点正在投射阴影。我还有一个位于其中一个阴影中的精灵。我希望能够判断用户是否将精灵从阴影中移到灯光中。无论如何要迅速做到这一点?谢谢。

4

1 回答 1

0

不幸的是,SpriteKit 中仍然不包含此功能,但是可以通过一些注意事项实现一个体面的解决方案。

为了确定一个精灵是否投射阴影,它的属性“通过执行逻辑与运算shadowCastBitMask来测试灯光的属性”。categoryBitMaskSpriteKit 似乎为光照和物理体计算生成完全相同的遮罩数据,基于文档中对shadowColor定义的属性的描述SKLightNode

计算光照时,会创建阴影,就像从光照节点的位置投射出光线一样。如果精灵投射阴影,则光线在与精灵的物理体相交时会被阻挡。否则,精灵的纹理用于生成遮罩,精灵节点纹理中任何具有非零 alpha 值的像素都会阻挡光线。

SKPhysicsWorld有一种方法,enumerateBodies(alongRayStart:end:using:),可以高效地执行这种光线相交测试。这意味着我们可以测试一个精灵是否被任何具有物理实体的精灵所遮蔽。所以我们可以写一个这样的方法来扩展SKSpriteNode

func isLit(by light: SKLightNode) -> Bool {
    guard light.isEnabled else {
        return false
    }

    var shadowed = false

    scene?.physicsWorld.enumerateBodies(alongRayStart: light.position, end: position) { (body, _, _, stop) in
        if let sprite = body.node as? SKSpriteNode, light.categoryBitMask & sprite.shadowCastBitMask != 0 {
            shadowed = true

            stop.pointee = true
        }
    }

    if shadowed {
        return false
    } else {
        return true
    }
}

我们还可以检索场景中的哪些灯光正在照亮特定的精灵:

func lights(affecting sprite: SKSpriteNode) -> [SKLightNode] {
    let lights = sprite.scene?.children.flatMap { (node) -> SKLightNode? in
        node as? SKLightNode
    } ?? []

    return lights.filter { (light) -> Bool in
        sprite.isLit(by: light)
    }
}

如果 SpriteKit 提供一种方法来检索这些信息而不将其耦合到物理 API,或者要求开发人员滚动他们自己的光线投射实现,那就太好了。

于 2017-08-05T05:39:32.830 回答