完成块从未在组的SKAction序列结束时调用

时间:2016-12-05 21:06:03

标签: swift sprite-kit closures skaction completion

为什么从未调用完成?

我非常抱歉,代码转储......因为我不知道为什么每个部分都有效,除了调用完成。

除完成外,所有未调用完成的SKAction都会运行。就是这样:

curtain.run(fadeMoveWipeAndReveal, completion: {onDone(), print("I'm done!!!!")})

在以下课程中:

import SpriteKit

class WipeCurtain: SKSpriteNode {

    var wipeCurtainBase: SKSpriteNode?
    var returnable = SKNode()
    var moveRight = SKAction()
    var node = SKNode()

    init(     color: SKColor,
              size: CGSize,
              time: TimeInterval,
              reveal: @escaping () -> (),
              onDone: @escaping () -> ())
    {
        super.init(texture: nil, color: color, size: size)
        wipeCurtainBase = SKSpriteNode(color: color, size: size)

        let show = SKAction.run(reveal)

        let fadeIn = createFadeIn(duration: time)
        let moveRight = createMoveRight(duration: time)
        let wipeRight = createWipeRight(duration: time)

        let fadeAndMove = SKAction.group( [ fadeIn, moveRight ] )
        let wipeAndReveal = SKAction.group( [ show, wipeRight ] )
        let fadeMoveWipeAndReveal = SKAction.sequence( [ fadeAndMove, wipeAndReveal ] )

        if let  curtain = self.wipeCurtainBase {
            curtain.anchorPoint = CGPoint(x: 1, y: 0)
            curtain.position = CGPoint(x: frame.size.width, y: 0)
            curtain.zPosition = -1
            curtain.name = "wipe"
            curtain.run(fadeMoveWipeAndReveal, completion: {
                onDone()
                print("I'm done!!!!")
            })
        }
    }

    func createFadeIn(duration: TimeInterval) -> SKAction {
        let fadeIn = SKEase
            .fade(
                easeFunction: .curveTypeLinear,
                easeType: .easeTypeIn,
                time: duration,
                fromValue: 0,
                toValue: 1
            )
        return fadeIn
        }

    func createMoveRight(duration: TimeInterval) -> SKAction {
        let moveRight = SKEase
            .move(
                easeFunction: .curveTypeExpo,
                easeType: .easeTypeOut,
                duration: duration,
                origin: CGPoint(
                    x: 0,
                    y: 0),
                destin: CGPoint(
                    x: frame.size.width,
                    y: 0)
            )
        return moveRight
        }

    func createWipeRight(duration: TimeInterval) -> SKAction {
        let wipeRight = SKEase
            .createFloatTween(
                start: 1,
                ender: 0,
                timer: duration,
                easer: SKEase
                    .getEaseFunction(
                        .curveTypeExpo,
                        easeType: .easeTypeOut
                    ),
            setterBlock: {(node, i) in
                node.xScale = i}
            )
        return wipeRight
        }

    func wipeWith() -> SKNode {
        if let curtain = wipeCurtainBase?.copy() as! SKSpriteNode? {
            returnable = curtain
            }
        return returnable
        }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

更新

以下是使这项工作的gameScene,Xcode SpriteKit模板的一个非常轻微的修改版本:

import SpriteKit
import GameplayKit

class GameScene: SKScene {

    private var swipe: WipeCurtain?

    override func didMove(to view: SKView) {
        swipe = WipeCurtain(color: .brown, size: frame.size, time: 1,
            reveal: self.showOrNot,
            onDone: self.previewer
            )
        }

    func showOrNot(){
        print(" Deciding to show or not.......")
        }

    func previewer(){
        print("now running the previewer")
        }

    func touchDown(atPoint pos : CGPoint) {
        addChild(swipe!.wipeWith())
        }

    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        for t in touches { self.touchDown(atPoint: t.location(in: self)) }
        }
}

3 个答案:

答案 0 :(得分:4)

在最终序列中使用run(block :() - &gt;())操作可获得所需的结果,但仍然无法理解为什么永远不会调用完成。这只是一种解决方法:

import SpriteKit

class WipeCurtain: SKSpriteNode {

    var wipeCurtainBase: SKSpriteNode?
    var returnable = SKNode()
    var moveRight = SKAction()
    var node = SKNode()

    init(     color: SKColor,
              size: CGSize,
              time: TimeInterval,
              reveal: @escaping () -> (),
              onDone: @escaping () -> ())
    {
        super.init(texture: nil, color: color, size: size)
        wipeCurtainBase = SKSpriteNode(color: color, size: size)

        let show = SKAction.run( reveal )
        let endBlock = SKAction.run( onDone )
        let fadeIn = createFadeIn(duration: time)
        let moveRight = createMoveRight(duration: time)
        let wipeRight = createWipeRight(duration: time)
        let fadeAndMove = SKAction.group( [ fadeIn, moveRight ] )
        let wipeAndReveal = SKAction.group( [ show, wipeRight ] )
        let fadeMoveWipeAndReveal = SKAction.sequence( 
                [ fadeAndMove, wipeAndReveal, endBlock ]
            )

        if let  curtain = self.wipeCurtainBase {
            curtain.anchorPoint = CGPoint(x: 1, y: 0)
            curtain.position = CGPoint(x: frame.size.width, y: 0)
            curtain.zPosition = -1
            curtain.name = "wipe"
            curtain.run(fadeMoveWipeAndReveal)
        }
    }

    func createFadeIn(duration: TimeInterval) -> SKAction {
        let fadeIn = SKEase
            .fade(
                easeFunction: .curveTypeLinear,
                easeType: .easeTypeIn,
                time: duration,
                fromValue: 0,
                toValue: 1
            )
        return fadeIn
        }

    func createMoveRight(duration: TimeInterval) -> SKAction {
        let moveRight = SKEase
            .move(
                easeFunction: .curveTypeExpo,
                easeType: .easeTypeOut,
                duration: duration,
                origin: CGPoint(
                    x: 0,
                    y: 0),
                destin: CGPoint(
                    x: frame.size.width,
                    y: 0)
            )
        return moveRight
        }

    func createWipeRight(duration: TimeInterval) -> SKAction {
        let wipeRight = SKEase
            .createFloatTween(
                start: 1,
                ender: 0,
                timer: duration,
                easer: SKEase
                    .getEaseFunction(
                        .curveTypeExpo,
                        easeType: .easeTypeOut
                    ),
            setterBlock: {(node, i) in
                node.xScale = i}
            )
        return wipeRight
        }

    func wipeWith() -> SKNode {
        if let curtain = wipeCurtainBase?.copy() as! SKSpriteNode? {
            returnable = curtain
            }
        return returnable
        }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

答案 1 :(得分:2)

我想把注意力集中在以下几行:

enter image description here

违规代码是:

curtain.run(fadeMoveWipeAndReveal, completion: {
                onDone()
                print("I'm done!!!!")
})

您可以将fadeMoveWipeAndReveal替换为show之类的其他操作:

let show = SKAction.run(reveal)

您将看到永远不会调用完成。为什么?因为它在WipeCurtain初始化期间第一次运行但操作在完成之前被删除,所以下次重新调用此操作以运行touchesBegan时,将永远不会调用完成。

您可以通过在curtain.run代码中添加断点并启动项目来测试它:

enter image description here

正如您可以看到断点在初始化期间立即停止项目,在此阶段,操作在完成之前被删除。

关于您的解决方法,这是正确的,它可以正常运行,因为您删除completion并使用每次调用时都正确执行的SKAction.sequence

<强>详情: copy方法适合我,我怀疑有些人有问题,因为在互联网上有更多关于SKEase的版本,可能其中一些可能有问题,但this版本运行良好通过以下两个屏幕截图:

enter image description here

enter image description here

答案 2 :(得分:1)

.copy()不会以这种方式复制块或闭包。这不是一个错误,苹果缺乏计划,或者他们没有向任何人透露的一些原则。它可能是NSObject的东西,因为你不能持久阻塞,或者使用NSCoder(afaik)。

从Xcode 8之前的角度来看,除了CRASH之外,这一切都不应该在所有AFAIK上做任何事情。你只需要调用一次初始化程序(用curtain实际工作的东西),你没有& #39; t最初将它添加到场景中。

我建议采取不同的方法,或者在你提出时使用解决方法。也许初始化新节点甚至比.copy()更快。需要进行测试才能看出它的性能如何。

赏金更新:

我的猜测是SKNode的NSObject和NSCoder一致性......所有节点都符合NSCoder,你不能将NSCoder与闭包/块一起使用,至少不是我知道的..甚至试图转换为/来自NSData或NSString。