完成块从不在 SKAction 组序列的末尾调用
Completion block never called at end of SKAction sequence of groups
为什么从未调用完成?
我对此感到非常抱歉,一个代码转储...因为我不知道为什么每个部分都有效,除了调用完成。
未调用其完成的 SKAction 运行,除了完成。是这样的:
curtain.run(fadeMoveWipeAndReveal, completion: {onDone(), print("I'm done!!!!")})
在以下class内:
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)) }
}
}
在最终序列中使用 运行(block: () -> ()) 操作获得了预期的结果,但仍然无法帮助我理解为什么永远不会调用完成。这只是一个解决方法:
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")
}
}
.copy()
不会以这种方式复制块或闭包。这要么是一个错误,要么是 Apple 缺乏计划,要么是他们没有向任何人透露的一些原则。它可能是 NSObject 的东西,因为你也不能持久化块,或者将 NSCoder 与它们一起使用 (afaik)。
从 Xcode 8 之前的角度来看,none 这应该做任何事情 AFAIK,除了 CRASH.. 你只调用一次初始化程序(实际工作的东西curtain
), 而你一开始并没有将它添加到场景中。
我建议对此采取不同的方法,或者使用您提出的解决方法。也许初始化一个新节点甚至比 .copy()
更快。需要进行测试以了解其性能。
赏金更新:
我的猜测是 SKNode 的 NSObject 和 NSCoder 一致性......所有节点都符合 NSCoder 并且你不能将 NSCoder 与闭包/块一起使用,至少我不知道......甚至试图转换 to/from NSData 或 NSString.
我想让你注意以下几行:
违规代码是:
curtain.run(fadeMoveWipeAndReveal, completion: {
onDone()
print("I'm done!!!!")
})
您可以将 fadeMoveWipeAndReveal
替换为其他操作之一,例如 show
:
let show = SKAction.run(reveal)
并且您会看到从未调用完成。为什么?因为它 运行 第一次是在 WipeCurtain 初始化期间,但动作在它完成之前被删除,所以下次你通过 touchesBegan
重新调用这个到 运行 时,将永远不会调用完成.
您可以通过在 curtain.run
代码中放置断点并启动项目来测试它:
如您所见断点在初始化期间立即停止项目,在此阶段,操作在完成之前被删除。
关于您的解决方法,这是正确的,它之所以有效,是因为您删除了 completion
并使用了每次调用时都能正确执行的简单 SKAction.sequence
。
详情:
copy
方法对我有用,我怀疑有些人对它有问题,因为在互联网上有更多关于 SKEase 的版本,也许其中一些可能有问题,但 this 版本效果很好,正如所展示的通过以下两个屏幕截图:
为什么从未调用完成?
我对此感到非常抱歉,一个代码转储...因为我不知道为什么每个部分都有效,除了调用完成。
未调用其完成的 SKAction 运行,除了完成。是这样的:
curtain.run(fadeMoveWipeAndReveal, completion: {onDone(), print("I'm done!!!!")})
在以下class内:
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)) }
}
}
在最终序列中使用 运行(block: () -> ()) 操作获得了预期的结果,但仍然无法帮助我理解为什么永远不会调用完成。这只是一个解决方法:
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")
}
}
.copy()
不会以这种方式复制块或闭包。这要么是一个错误,要么是 Apple 缺乏计划,要么是他们没有向任何人透露的一些原则。它可能是 NSObject 的东西,因为你也不能持久化块,或者将 NSCoder 与它们一起使用 (afaik)。
从 Xcode 8 之前的角度来看,none 这应该做任何事情 AFAIK,除了 CRASH.. 你只调用一次初始化程序(实际工作的东西curtain
), 而你一开始并没有将它添加到场景中。
我建议对此采取不同的方法,或者使用您提出的解决方法。也许初始化一个新节点甚至比 .copy()
更快。需要进行测试以了解其性能。
赏金更新:
我的猜测是 SKNode 的 NSObject 和 NSCoder 一致性......所有节点都符合 NSCoder 并且你不能将 NSCoder 与闭包/块一起使用,至少我不知道......甚至试图转换 to/from NSData 或 NSString.
我想让你注意以下几行:
违规代码是:
curtain.run(fadeMoveWipeAndReveal, completion: {
onDone()
print("I'm done!!!!")
})
您可以将 fadeMoveWipeAndReveal
替换为其他操作之一,例如 show
:
let show = SKAction.run(reveal)
并且您会看到从未调用完成。为什么?因为它 运行 第一次是在 WipeCurtain 初始化期间,但动作在它完成之前被删除,所以下次你通过 touchesBegan
重新调用这个到 运行 时,将永远不会调用完成.
您可以通过在 curtain.run
代码中放置断点并启动项目来测试它:
如您所见断点在初始化期间立即停止项目,在此阶段,操作在完成之前被删除。
关于您的解决方法,这是正确的,它之所以有效,是因为您删除了 completion
并使用了每次调用时都能正确执行的简单 SKAction.sequence
。
详情:
copy
方法对我有用,我怀疑有些人对它有问题,因为在互联网上有更多关于 SKEase 的版本,也许其中一些可能有问题,但 this 版本效果很好,正如所展示的通过以下两个屏幕截图: