在 Spritekit 中扩展圆形切口过渡效果

Posted

技术标签:

【中文标题】在 Spritekit 中扩展圆形切口过渡效果【英文标题】:Expanding circle cutout transition effect in Spritekit 【发布时间】:2017-02-01 16:31:24 【问题描述】:

我正在尝试获得类似于任天堂在 sprite kit 中的超级马里奥奔跑游戏中的过渡效果

我只想要一个圆形切口来扩展以显示下一个场景或仅显示当前场景。这不能作为标准 ios sprite kit 转换之一使用。

我已经用一个完全黑色的图层覆盖整个场景并使用 SKCropNode 动画一个扩大的圆圈来显示场景。

以下是didMove(to view: SKView)中的代码

let fullScreen = SKSpriteNode(color: .black, size: self.size)
let mask = SKSpriteNode(color: .black, size: self.size)
let circle = SKShapeNode(circleOfRadius: self.size.height / 2)
circle.fillColor = .white
circle.blendMode = .subtract
circle.setScale(0.001)
circle.isHidden = true
circle.name = "circleShape"
mask.addChild(circle)

let crop = SKCropNode()
crop.position = CGPoint(x: self.size.width / 2, y: self.size.height / 2)
crop.maskNode = mask
crop.name = "cropNode"
crop.addChild(fullScreen)
crop.zPosition = 100

self.addChild(crop)

let waitAction = SKAction.wait(forDuration: 2.0)
let callAction = SKAction.run(
    let cropNode = self.childNode(withName: "cropNode") as! SKCropNode
    let maskNode = cropNode.maskNode as! SKSpriteNode
    let circleNode = maskNode.childNode(withName: "circleShape") as! SKShapeNode
    circleNode.isHidden = false
    let scaleAction = SKAction.scale(to: 2.0, duration: 2.0)
    scaleAction.timingFunction = scaleAction.easeInQuartic
    circleNode.run(scaleAction, completion: 
        cropNode.removeFromParent()
    )
)
let seqAction = SKAction.sequence([waitAction,callAction])
self.run(seqAction)

这在模拟器中有效,但在设备上运行时无效(iPad pro 2016 和最新的 iOS 10)。在设备上,缩放动作完成后,没有出现扩大的圆圈,黑色的覆盖层消失了。

我的问题:

1) 为什么这不能在设备上运行,只能在模拟器上运行?

2) 有没有更好更有效的方法来实现 sprite kit 中的这种过渡?

提前致谢。

【问题讨论】:

【参考方案1】:

可能有 100 种不同的方法可以实现这一目标。我的方法比你的好吗?可能不是,但这对我在模拟器和 iPad 上都有效。

只需以 0.0 的持续时间过渡到您的场景,然后从 didMove(to view:) 运行这个乐趣

你需要一个单独的圆圈图像的原因是它在放大时保持其漂亮的清晰边缘

bgOverlay 只是从一个完全黑屏开始,然后应用伪过渡

func circleTransition() 

    let bgMask = SKSpriteNode(texture: SKTexture(imageNamed: "bg_mask"), color: .clear, size: CGSize(width: self.size.width, height: self.size.height))
    bgMask.position = CGPoint(x: self.size.width / 2, y: self.size.height / 2)
    bgMask.zPosition = 5000
    self.addChild(bgMask)

    let transitionCircle = SKSpriteNode(texture: SKTexture(imageNamed: "transition_circle"), color: .clear, size: CGSize(width: 13, height: 13))
    transitionCircle.position = CGPoint.zero
    transitionCircle.zPosition = 1
    bgMask.addChild(transitionCircle)

    let bgOverlay = SKSpriteNode(texture: SKTexture(imageNamed: "bg_overlay"), color: .clear, size: CGSize(width: self.size.width, height: self.size.height))
    bgOverlay.position = CGPoint(x: self.size.width / 2, y: self.size.height / 2)
    bgOverlay.zPosition = 5001
    self.addChild(bgOverlay)

    bgMask.run(SKAction.sequence([SKAction.scale(to: 100.0, duration: 2.0), SKAction.removeFromParent()]))
    bgOverlay.run(SKAction.sequence([SKAction.fadeOut(withDuration: 0.1), SKAction.removeFromParent()]))

【讨论】:

我看不到您的方法如何达到预期的结果。您正在创建 bgMask、transitionCircle 和 bgOverlay 精灵节点。然后,您将添加 transitionCircle 作为 bgMask 的子项。圆圈在哪里剪下来?你是如何在 bgMask 上打一个圆孔的?我没有看到任何混合模式或 SKCropNode 的使用。 bgMask、transitionCircle、bgOverlay的图片内容是什么? 3 张图片,16 行代码,1 个函数。尝试这段代码可能比写评论更容易。 没有混合模式,没有 CropNodes。只是简单的眼睛糖果。过渡圆图像有一个透明圆(不是本页显示的白色)。至于图片内容……你可以看到图片,对吗? 是的,我可以看到图像。但我不明白哪个是哪个。所以据我了解,bgmask 是一个黑色图像,透明正方形为 13px x 13px,过渡圆是一个透明圆,周围有黑色,bgOverlay 只是一个用于淡出的黑框。我对么?我希望有一种更有效的内存方式。也许我可以用 SKShapeNodes 而不是 SKSpriteNodes 来做到这一点。 SKSpriteNodes 意味着它将为设备屏幕分辨率大小的纹理占用不必要的内存。 你知道使用形状节点的内存占用是多少吗? (假设你可以让它与他们一起工作)。我通过 tinyPNG 运行图像,最终 3 张图像的大小为 38.6kb。这是一个相当低的足迹。我昨天在您的设置上花了一个多小时试图使用 SKCropNode 和混合模式,但没有成功。因此,我认为;回答您的问题是否有更好的方法。是的,这更好,因为它有效。【参考方案2】:

我最终使用了带有片段着色器的 SKSpriteNode。以下是主场景中的代码:

let fullScreen = SKSpriteNode(color: UIColor.clear, size: CGSize(width: self.size.width, height: self.size.height))
fullScreen.position = CGPoint(x: self.size.width / 2.0, y: self.size.height / 2.0)
fullScreen.zPosition = 200
self.addChild(fullScreen)
let shader = SKShader(fileNamed: "transitionShader.fsh")
shader.attributes = [SKAttribute(name: "a_sprite_size", type: .vectorFloat2),
                     SKAttribute(name:"a_duration", type: .float)]
fullScreen.shader = shader
let spriteSize = vector_float2(Float(fullScreen.frame.size.width),Float(fullScreen.frame.size.height))
fullScreen.setValue(SKAttributeValue(vectorFloat2: spriteSize),forAttribute: "a_sprite_size")
fullScreen.setValue(SKAttributeValue(float: Float(mainGameTransitionDuration)), forAttribute: "a_duration")

以下是位于transitionShader.fsh文件中的着色器代码

#define M 1000.0 // Multiplier

void main()

    float aspect = a_sprite_size.y / a_sprite_size.x;
    float u = v_tex_coord.x;
    float v = v_tex_coord.y * aspect;
    vec2 uv = vec2(u,v) * M;
    vec2 center = vec2(0.60,0.55 * aspect) * M;
    float t = u_time / a_duration;
    if ( t < 1.0 )
    
        float easeIn = pow(t,5.0);
        float radius = easeIn * 2.0 * M;
        float d = length(center - uv) - radius;
        float a = clamp(d, 0.0, 1.0);
        gl_FragColor = vec4(0.0,0.0,0.0, a);
    
    else
    
        gl_FragColor = vec4(0.0,0.0,0.0,0.0);
    

它似乎可以正常工作,并且在 CPU 或内存上似乎并不昂贵。

【讨论】:

即使使用 Swift 5 也能很好地工作

以上是关于在 Spritekit 中扩展圆形切口过渡效果的主要内容,如果未能解决你的问题,请参考以下文章

新活动的圆形展示过渡

SpriteKit:在场景过渡中暂停动画

在 SpriteKit 中不工作的过渡

自定义SpriteKit“开门”、“翻页”过渡

SpriteKit:检测当新场景显示以后

vue2.0 transition 多个元素嵌套使用过渡