Unexpectedly found nil while unwrapping an Optional value - boxNode.addChildNode(importedBox!) - ios

I have some code that runs a for loop to create 2 objects and stuffs them into the boxArray then adds them to the scene. The first time through the code works fine to create one box but during the addBox(num: Int) function to create the second box it crashes with the error in the subject line. Its this line that is responsible "boxNode.addChildNode(importedBox!)" but Im at a loss as to why since it works the first time.
importedSceneBox = SCNScene(named: "art.scnassets/boxObject.dae")
for var i = 0; i <= 1; ++i {
let boxNode = self.addBox(i)
boxArray.append(boxNode)
theScene.rootNode.addChildNode(boxArray[i])
}
func addBox(num: Int) -> SCNNode{
let boxNode = SCNNode()
let importedBox = importedSceneBox.rootNode.childNodeWithName("pCube4", recursively: false)
importedBox?.scale = SCNVector3Make(70, 70, 70)
boxNode.addChildNode(importedBox!)
boxNode.position = SCNVector3Make(5, 100, 3)
let collisionBox = SCNBox(width: 5.0, height: 5.0, length: 5.0, chamferRadius: 0)
boxNode.physicsBody?.physicsShape = SCNPhysicsShape(geometry: collisionBox, options: nil)
boxNode.physicsBody = SCNPhysicsBody.dynamicBody()
boxNode.physicsBody?.mass = 1
boxNode.physicsBody?.restitution = 0.8
boxNode.physicsBody?.damping = 0.5
boxNode.name = "dice" + String(num)
boxNode.physicsBody?.allowsResting = true
return boxNode
}

hi your importedBox is an optional meaning it can have a value or it can be nil.
you are using "!"(IUO implicitly unwrapped optional) behind importedBox which means that your telling the compiler that your sure its not be nil. How ever in this case it WAS nil thus the crash "found nil when unwrapping optional"
So recap importedBox is nil but you marked it with ! so the compiler think it will never have a nil value but a nil was found thus the crash.
to fix this you can use guard or optional binding
Guard example
func addBox(num: Int) -> SCNNode{
let boxNode = SCNNode()
let importedBox = importedSceneBox.rootNode.childNodeWithName("pCube4", recursively: false)
importedBox?.scale = SCNVector3Make(70, 70, 70)
guard let unwrapBox = importedBox else {
//do your error handling here
return SCNNode()
}
boxNode.addChildNode(unwrapBox)
boxNode.position = SCNVector3Make(5, 100, 3)
let collisionBox = SCNBox(width: 5.0, height: 5.0, length: 5.0, chamferRadius: 0)
boxNode.physicsBody?.physicsShape = SCNPhysicsShape(geometry: collisionBox, options: nil)
boxNode.physicsBody = SCNPhysicsBody.dynamicBody()
boxNode.physicsBody?.mass = 1
boxNode.physicsBody?.restitution = 0.8
boxNode.physicsBody?.damping = 0.5
boxNode.name = "dice" + String(num)
boxNode.physicsBody?.allowsResting = true
return boxNode
}
optional binding example
func addBox(num: Int) -> SCNNode{
let boxNode = SCNNode()
let importedBox = importedSceneBox.rootNode.childNodeWithName("pCube4", recursively: false)
importedBox?.scale = SCNVector3Make(70, 70, 70)
if let unwrapBox = importedBox else {
boxNode.addChildNode(unwrapBox)
boxNode.position = SCNVector3Make(5, 100, 3)
let collisionBox = SCNBox(width: 5.0, height: 5.0, length: 5.0, chamferRadius: 0)
boxNode.physicsBody?.physicsShape = SCNPhysicsShape(geometry: collisionBox, options: nil)
boxNode.physicsBody = SCNPhysicsBody.dynamicBody()
boxNode.physicsBody?.mass = 1
boxNode.physicsBody?.restitution = 0.8
boxNode.physicsBody?.damping = 0.5
boxNode.name = "dice" + String(num)
boxNode.physicsBody?.allowsResting = true
return boxNode
}else{
//do your error handling here
return SCNNode()
}
}
also as a side note in the error handling cases we are creating a default value since your function returns a SCNNode and not a SCNNode optional.
Alternatively you can return nil if you change your function signature to be
func addBox(num: Int) -> SCNNode? {
if let unwrappedBox = importedBox {
//do stuff
}else{
return nil
}
}

Related

ARKit not working as expected, If the surface is plain, like a one-colored wall, it’s not detected

I am working on one application which has one functionality like user needs to show the paintings(in 2D images) on the surface with ARKit.
I have implemented it but it has some issues.
It works only for the surfaces that have distinguishable features — e.g. hanging photos, text or art. If the surface is plain, like a one-colored wall, or has only a few small features, it’s not detected. Which means that most of the walls are invisible to ARKit.
Need some solution for this. Any help would be appreciated.
{
self.node.removeFromParentNode()
node.eulerAngles.z = 0
//node.scale = SCNVector3(1.0, 1.0, 1.0)
self.viewAR.backgroundColor = UIColor.clear
self.imageView.contentMode = .scaleAspectFit
self.viewAR.frame = CGRect(x: 0.0, y: 0.0, width: 250, height: 250)
self.imageView.frame = self.viewAR.frame
self.node.geometry = SCNPlane(width: 0.05, height: 0.05)
if selectedIndexPath.row < 0 {
self.imageView.layer.borderColor = UIColor.clear.cgColor
}
else {
self.imageView.layer.borderColor = colors[selectedIndexPath.row].cgColor
}
self.imageView.layer.borderWidth = 10.0
self.viewAR.addSubview(self.imageView)
if let resWidth = self.newWork?.getSetArtWidth, let resHeight = self.newWork?.getSetArtHeight {
if resWidth > 0.0 && resHeight > 0.0 {
self.viewAR.addSubview(self.lblDimension)
self.lblDimension.backgroundColor = colors[selectedIndexPath.row]
self.lblDimension.textColor = UIColor.white
self.lblDimension.text = "Size = \(resWidth) mm * \(resHeight) mm"
}
}
if aRImageList.count != 0 {
if let imgUrl = aRImageList[imageIndex] as? String {
if let resUrl = URL(string: imgUrl){
self.imageView.sd_setImage(with: resUrl, placeholderImage: LISTING_PLACEHOLDER , completed: { (image, error, disk, imageURL) in
if error == nil {
self.imageView.image = image
if let image = image {
self.setImageSize(image)
}
self.node.geometry?.firstMaterial?.isDoubleSided = true
self.node.geometry?.firstMaterial?.diffuse.contents = self.viewAR.asImage()
}
})
} else {
self.imageView.image = LISTING_PLACEHOLDER
}
}
else if let resImage = aRImageList[imageIndex] as? UIImage {
self.imageView.image = resImage
}
//self.imageView.image = aRImageList[imageIndex]
}
node.geometry?.firstMaterial?.diffuse.contents = viewAR.asImage()//aRImageList[imageIndex]//viewToAdd
if isHorizontal == true {
//let resScale = node.scale
// let resAngle = node.eulerAngles.z
node.position = SCNVector3(hitTestResult.worldTransform.columns.3.x, hitTestResult.worldTransform.columns.3.y, hitTestResult.worldTransform.columns.3.z)
//node.scale = resScale
//node.eulerAngles.z = resAngle
node.pivot = SCNMatrix4Rotate(node.pivot, Float.pi, 0, 1, 0)
let constraint = SCNLookAtConstraint(target:sceneView.pointOfView)
constraint.isGimbalLockEnabled = true
node.constraints = [constraint]
}
else {
node.constraints = nil
node.pivot = SCNMatrix4MakeTranslation(0,0,0)
let resScale = node.scale
//let resAngle = node.eulerAngles.z
node.transform = SCNMatrix4(hitTestResult.anchor!.transform)
node.eulerAngles = SCNVector3(node.eulerAngles.x + (-Float.pi / 2), node.eulerAngles.y, node.eulerAngles.z)
node.position = SCNVector3(hitTestResult.worldTransform.columns.3.x, hitTestResult.worldTransform.columns.3.y, hitTestResult.worldTransform.columns.3.z + 0.07)
node.scale = resScale
// node.eulerAngles.z = resAngle
}
sceneView.scene.rootNode.addChildNode(node)
planeNode.removeFromParentNode()
for removeNode in self.planeNodes {
removeNode.removeFromParentNode()
}
}

"lldb" crash with XCODE & ARKIT : 0 __ UpdateAudioTransform

I have written a game using ARKIT that pops up random nodes (with drones) in the 3d space around the player, using ARKIT, and I get an incomprehensible crash, as only thing in the console is "lldb" , and the rest of the crash details are on the screenshot attached (I am still a newbie in Swift so not able to debug it).
The crash happens when there's a lot of nodes on the screen (maybe 20-30) and the FPS drops - ie happens "mid - game" and the FPS drops a lot.
Can someone point me to the right direction for this crash?
The part of the code that is in my opinion relevant is the function that spawns the random 3d nodes (they also have SCNActions attached that play sounds when they're tapped - perhaps this could be relevant as the left hand side debugger opens with that line highlighted when the crash occurs, as per the photo attached). In case it is also relevant, the program uses some SCNParticleSystem calls as well. Attaching relevant code, and also the snapshot of the crash screen:
var droneSound : SCNAudioSource()
override func viewDidLoad() {
super.viewDidLoad()
droneSound = SCNAudioSource(named: "Sounds/drone1.wav")!
playDroneSound = SCNAction.playAudio(self.droneSound, waitForCompletion: true)
}
func startGame() {
DispatchQueue.main.async {
self.spawnTimer = Timer.scheduledTimer(timeInterval: TimeInterval(self.randomFloat(min: 2.5, max: 5)), target: self, selector: #selector(self.spawnDronesInterim), userInfo: nil, repeats: true)
}
}
#objc func spawnDronesInterim() {
for _ in 0...5 {
spawnDrone()
}
}
#objc func spawnDrone() {
let newDroneScene = SCNScene(named: "Ar.scnassets/DroneScene.scn")!
var newDroneNode = newDroneScene.rootNode.childNode(withName: "Drone", recursively: false)!
newDroneNode.name = "Drone\(self.droneCounter)"
newDroneNode = newDroneScene.rootNode.childNode(withName: "Drone\(self.droneCounter)", recursively: false)!
newDroneNode.position.x = newDroneNode.presentation.position.x + self.randomFloat(min: -10, max: 10)
newDroneNode.position.y = newDroneNode.presentation.position.y + self.randomFloat(min: -1, max: 5)
newDroneNode.position.z = newDroneNode.presentation.position.z + self.randomFloat(min: -10, max: 10)
let move1 = SCNAction.move(to: SCNVector3((self.randomFloat(min: -7, max: 7)), (self.randomFloat(min: 1, max: 5)), (self.randomFloat(min: -7, max: 7))), duration: 15)
let disappearMove = SCNAction.move(to: SCNVector3((self.randomFloat(min: -10, max: 10)), (self.randomFloat(min: 1, max: 5)), (self.randomFloat(min: -10, max: 10))), duration: 3)
let rotateAction = SCNAction.run { (SCNNode) in
let rotate = SCNAction.rotateBy(x: 0, y: CGFloat(360.degreesToRadians), z: 0, duration: 2)
newDroneNode.runAction(rotate)
}
let removeIt = SCNAction.removeFromParentNode()
let sequence = SCNAction.sequence([waitFive,move1,rotateAction,waitFive,disappearMove,waitTwo,removeIt])
newDroneNode.runAction(sequence)
self.sceneView.scene.rootNode.addChildNode(newDroneNode)
if self.droneCounter >= 5 {
self.sceneView.scene.rootNode.childNode(withName: "Drone\(self.droneCounter)", recursively: true)!.runAction(SCNAction.repeatForever(self.playDroneSound))
}
self.droneCounter += 1
}
when user taps on one of the 3d nodes, this gets called:
func handleExplosion (node : SCNNode) {
self.sceneView.scene.rootNode.childNode(withName: node.name!, recursively: true)!.runAction(self.playExplosionSound)
node.opacity = 0
print (self.sceneView.scene.rootNode.position)
let confetti = SCNParticleSystem(named: "Ar.scnassets/confetti.scnp", inDirectory: nil)
confetti?.loops = false
confetti?.particleLifeSpan = 1.5
confetti?.emitterShape = node.geometry
let confettiNode = SCNNode()
confettiNode.addParticleSystem(confetti!)
confettiNode.position = node.presentation.position
self.sceneView.scene.rootNode.addChildNode(confettiNode)
let fire = SCNParticleSystem(named: "Ar.scnassets/fire", inDirectory: nil)
fire?.loops = false
fire?.particleLifeSpan = 0.1
fire?.emitterShape = node.geometry
let fireNode = SCNNode()
fireNode.addParticleSystem(fire!)
fireNode.position = node.presentation.position
self.sceneView.scene.rootNode.addChildNode(fireNode)
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
node.removeFromParentNode()
self.killedLabel.text = "Killed: \(self.killedCount)"
self.dronesOut -= 1
}
}
Any pointer to the right direction for solving this crash would be greatly appreciated]1

Initializer for conditional binding must have Optional type, not 'UIView'

From research on StackOverflow I've learned that this error is caused by attempting to bind a type that isn't an optional however it doesn't make sense in this situation because it used guard instead. Here's my code:
func animateTransition(_ transitionContext: UIViewControllerContextTransitioning) {
// Here, we perform the animations necessary for the transition
guard let fromVC = transitionContext.viewController(forKey: UITransitionContextFromViewControllerKey) else { return }
let fromView = fromVC.view
guard let toVC = transitionContext.viewController(forKey: UITransitionContextToViewControllerKey) else { return }
let toView = toVC.view
guard let containerView = transitionContext.containerView() else { return }
if self.presenting {
containerView.addSubview(toView)
}
let animatingVC = self.presenting ? toVC : fromVC
let animatingView = animatingVC.view
let appearedFrame = transitionContext.finalFrame(for: animatingVC)
var alpha: CGFloat = 1
if self.options.contains([.AlphaChange]) {
alpha = 0;
}
let initialAlpha = self.presenting ? alpha : 1
let finalAlpha = self.presenting ? 1: alpha
var dismissedFrame = appearedFrame
let startRect = CGRect(origin: appearedFrame.origin, size: containerView.bounds.size)
let offset = self.calculateStartPointOffset(startRect, options: self.options)
if options.contains([.Dissolve]) && !self.presenting {
dismissedFrame.size = containerView.bounds.size
dismissedFrame.origin = CGPointZero
} else {
dismissedFrame = CGRect(x: offset.x, y: offset.y, width: appearedFrame.width, height: appearedFrame.height)
}
let initialFrame = self.presenting ? dismissedFrame : appearedFrame
let finalFrame = self.presenting ? appearedFrame : dismissedFrame
animatingView?.frame = initialFrame
animatingView?.alpha = initialAlpha
let dumpingValue = CGFloat(self.options.contains([.Interactive]) ? 1 : 0.8)
UIView.animate(withDuration: self.transitionDuration(transitionContext), delay: 0, usingSpringWithDamping: dumpingValue, initialSpringVelocity: 0.2, options: [UIViewAnimationOptions.allowUserInteraction, UIViewAnimationOptions.beginFromCurrentState],
animations:
{ () -> Void in
animatingView?.frame = finalFrame
animatingView?.alpha = finalAlpha
})
{ (completed) -> Void in
if !self.presenting {
fromView?.removeFromSuperview()
self.tDelegate?.didDissmisedPresentedViewController()
}
let cancelled = transitionContext.transitionWasCancelled()
transitionContext.completeTransition(!cancelled)
}
}
Xcode show an error on this line:
guard let containerView = transitionContext.containerView() else { return }
transitionContext.containerView() was changed to return a non-optional, so you can't use it to initialize a variable in a conditional binding like a guard or if let.
You should remove the guard from that line:
let containerView = transitionContext.containerView()
The container view in which a presentation occurs. It is an ancestor of both the presenting and presented view controller's views.
This containerView is being passed to the animation controller and It's return a non-optional
NOTE:
if let/if var optional binding only works when the result of the right side of the expression is an optional. If the result of the right side is not an optional, you can not use this optional binding. The point of this optional binding is to check for nil and only use the variable if it's non-nil.

ios Swift - Group separated sprites with syncronized animation

I am trying to make a synchronized animation (a large video decomposed by frames on separated and smaller puzzle jigsaw parts). This game is a video puzzle. Here is the code I use in three parts by way of example:
func Anim_Puzzle13 (Node13 : SKPuzzle) {
let puzzle13 = SKAction.animateWithTextures(sheet_puzzle13.Puzzle13_(), timePerFrame: 0.066)
NPuzzle13 = Node13
NPuzzle13.runAction(SKAction.repeatActionForever(puzzle13))
NPuzzle13.position = CGPoint(x: 500, y: 400)
NPuzzle13.zPosition = 1
}
func Anim_Puzzle19 (Node19 : SKPuzzle) {
let puzzle19 = SKAction.animateWithTextures(sheet_puzzle19.Puzzle19_(), timePerFrame: 0.066)
NPuzzle19 = Node19
NPuzzle19.runAction(SKAction.repeatActionForever(puzzle19))
NPuzzle19.position = CGPoint(x: 600, y: 500)
NPuzzle19.zPosition = 1
}
func Anim_Puzzle30 (Node30 : SKPuzzle) {
let puzzle30 = SKAction.animateWithTextures(sheet_puzzle30.Puzzle30_(), timePerFrame: 0.066)
NPuzzle30 = Node30
NPuzzle30.runAction(SKAction.repeatActionForever(puzzle30))
NPuzzle30.position = CGPoint(x: 700, y: 600)
NPuzzle30.zPosition = 1
}
It works well but it does not synchronize between the animations and the video has no integrity. I searched for a long time for a solution to make the animations synchronize; I see two possibilities: first is to create a unique SKNode() with all the jigsaw parts inside, but I want to be able to move each jigsaw part independently and have had no success getting a synchronized animation with this method.
The other way seem to be to create a group with all the animations together but this doesn't work, and causes the application to stop.
Here is all the code I use:
import SpriteKit
import UIKit
import AVFoundation
import AVKit
import CoreFoundation
private let kpuzzleNodeName = "puzzle"
private let kdancing = "dancing"
class SKPuzzle: SKSpriteNode {
var name2:String = "";
}
class GameScene: SKScene {
var background = SKVideoNode(videoFileNamed: "Video_Socle.mov")
var selectedNode = SKPuzzle()
var player:AVPlayer?
var videoNode:SKVideoNode?
var NPuzzle13 = SKPuzzle()
var NPuzzle19 = SKPuzzle()
var NPuzzle30 = SKPuzzle()
var NPuzzle11 = SKPuzzle()
var NPuzzle29 = SKPuzzle()
var NPuzzle35 = SKPuzzle()
var puzzle13 = SKAction()
var puzzle19 = SKAction()
var puzzle30 = SKAction()
var puzzle11 = SKAction()
var puzzle29 = SKAction()
var puzzle35 = SKAction()
let sheet_puzzle13 = Puzzle13()
let sheet_puzzle19 = Puzzle19()
let sheet_puzzle30 = Puzzle30()
let sheet_puzzle11 = Puzzle11()
let sheet_puzzle29 = Puzzle29()
let sheet_puzzle35 = Puzzle35()
override init(size: CGSize) {
super.init(size: size)
// 1
self.background.name = kdancing
self.background.anchorPoint = CGPointZero
background.zPosition = 0
self.addChild(background)
// 2
let sheet = Statiques()
let sprite_dancing1 = SKSpriteNode(texture: sheet.Dancing1())
let sprite_dancing2 = SKSpriteNode(texture: sheet.Dancing2())
sprite_dancing1.name = kdancing
sprite_dancing2.name = kdancing
sprite_dancing1.position = CGPoint(x: 837, y: 752)
sprite_dancing1.zPosition = 2
sprite_dancing2.position = CGPoint(x: 1241, y: 752)
sprite_dancing2.zPosition = 2
background.addChild(sprite_dancing1)
background.addChild(sprite_dancing2)
let imageNames = [sheet.Puzzle13() , sheet.Puzzle19(), sheet.Puzzle30(), sheet.Puzzle11(), sheet.Puzzle29(), sheet.Puzzle35() ]
for i in 0..<imageNames.count {
let imageName = imageNames[i]
let sprite = SKPuzzle(texture: imageName)
sprite.name = kpuzzleNodeName
sprite.name2 = "\(i)"
let offsetFraction = (CGFloat(i) + 1.0)/(CGFloat(imageNames.count) + 1.0)
sprite.position = CGPoint(x: size.width * offsetFraction, y: size.height / 2)
sprite.zPosition = 3
background.addChild(sprite)
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch: AnyObject in touches {
let positionInScene = touch.locationInNode(self)
selectNodeForTouch(positionInScene)
}
}
override func didMoveToView(view: SKView) {
let urlStr = NSBundle.mainBundle().pathForResource("Video_Socle", ofType: "mov")
let url = NSURL(fileURLWithPath: urlStr!)
player = AVPlayer(URL: url)
NSNotificationCenter.defaultCenter().addObserverForName(AVPlayerItemDidPlayToEndTimeNotification, object: player!.currentItem, queue: nil)
{ notification in
let t1 = CMTimeMake(5, 100);
self.player!.seekToTime(t1)
self.player!.play()
}
videoNode = SKVideoNode(AVPlayer: player!)
videoNode!.position = CGPointMake(frame.size.width/2, frame.size.height/2)
videoNode!.size = CGSize(width: 2048, height: 1536)
videoNode!.zPosition = 0
background.addChild(videoNode!)
videoNode!.play()
let gestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(GameScene.handlePanFrom(_:)))
self.view!.addGestureRecognizer(gestureRecognizer)
}
func handlePanFrom(recognizer : UIPanGestureRecognizer) {
if recognizer.state == .Began {
var touchLocation = recognizer.locationInView(recognizer.view)
touchLocation = self.convertPointFromView(touchLocation)
self.selectNodeForTouch(touchLocation)
} else if recognizer.state == .Changed {
var translation = recognizer.translationInView(recognizer.view!)
translation = CGPoint(x: translation.x, y: -translation.y)
self.panForTranslation(translation)
recognizer.setTranslation(CGPointZero, inView: recognizer.view)
} else if recognizer.state == .Ended {
}
}
func degToRad(degree: Double) -> CGFloat {
return CGFloat(degree / 180.0 * M_PI)
}
func selectNodeForTouch(touchLocation : CGPoint) {
// 1
let touchedNode = self.nodeAtPoint(touchLocation)
if touchedNode is SKPuzzle {
// 2
if !selectedNode.isEqual(touchedNode) {
selectedNode.runAction(SKAction.rotateToAngle(0.0, duration: 0.1))
selectedNode = touchedNode as! SKPuzzle
// 3
if touchedNode.name! == kpuzzleNodeName {
let sequence = SKAction.sequence([SKAction.rotateByAngle(degToRad(-4.0), duration: 0.1),
SKAction.rotateByAngle(0.0, duration: 0.1),
SKAction.rotateByAngle(degToRad(4.0), duration: 0.1)])
selectedNode.runAction(SKAction.repeatActionForever(sequence))
}
}
}
}
func panForTranslation(translation : CGPoint) {
let position = selectedNode.position
if selectedNode.name! == kpuzzleNodeName {
selectedNode.position = CGPoint(x: position.x + translation.x * 2, y: position.y + translation.y * 2)
print (selectedNode.name)
print (selectedNode.name2)
if selectedNode.name2 == "0" {
Anim_Puzzle13(selectedNode)
}
print (selectedNode.name2)
if selectedNode.name2 == "1" {
Anim_Puzzle19(selectedNode)
}
print (selectedNode.name2)
if selectedNode.name2 == "2" {
Anim_Puzzle30(selectedNode)
}
print (selectedNode.name2)
if selectedNode.name2 == "3" {
Anim_Puzzle11(selectedNode)
}
print (selectedNode.name2)
if selectedNode.name2 == "4" {
Anim_Puzzle29(selectedNode)
}
print (selectedNode.name2)
if selectedNode.name2 == "5" {
Anim_Puzzle35(selectedNode)
}
}
}
func Anim_Puzzle13 (Node13 : SKPuzzle) {
let puzzle13 = SKAction.animateWithTextures(sheet_puzzle13.Puzzle13_(), timePerFrame: 0.066)
NPuzzle13 = Node13
NPuzzle13.runAction(SKAction.repeatActionForever(puzzle13))
NPuzzle13.position = CGPoint(x: 500, y: 400)
NPuzzle13.zPosition = 1
}
func Anim_Puzzle19 (Node19 : SKPuzzle) {
let puzzle19 = SKAction.animateWithTextures(sheet_puzzle19.Puzzle19_(), timePerFrame: 0.066)
NPuzzle19 = Node19
NPuzzle19.runAction(SKAction.repeatActionForever(puzzle19))
NPuzzle19.position = CGPoint(x: 600, y: 500)
NPuzzle19.zPosition = 1
}
func Anim_Puzzle30 (Node30 : SKPuzzle) {
let puzzle30 = SKAction.animateWithTextures(sheet_puzzle30.Puzzle30_(), timePerFrame: 0.066)
NPuzzle30 = Node30
NPuzzle30.runAction(SKAction.repeatActionForever(puzzle30))
NPuzzle30.position = CGPoint(x: 700, y: 600)
NPuzzle30.zPosition = 1
}
func Anim_Puzzle11 (Node11 : SKPuzzle) {
let puzzle11 = SKAction.animateWithTextures(sheet_puzzle11.Puzzle11_(), timePerFrame: 0.066)
NPuzzle11 = Node11
NPuzzle11.runAction(SKAction.repeatActionForever(puzzle11))
NPuzzle11.position = CGPoint(x: 800, y: 700)
NPuzzle11.zPosition = 1
}
func Anim_Puzzle29 (Node29 : SKPuzzle) {
let puzzle29 = SKAction.animateWithTextures(sheet_puzzle29.Puzzle29_(), timePerFrame: 0.066)
NPuzzle29 = Node29
NPuzzle29.runAction(SKAction.repeatActionForever(puzzle29))
NPuzzle29.position = CGPoint(x: 900, y: 800)
NPuzzle29.zPosition = 1
}
func Anim_Puzzle35 (Node35 : SKPuzzle) {
let puzzle35 = SKAction.animateWithTextures(sheet_puzzle35.Puzzle35_(), timePerFrame: 0.066)
NPuzzle35 = Node35
NPuzzle35.runAction(SKAction.repeatActionForever(puzzle35))
NPuzzle35.position = CGPoint(x: 1000, y: 900)
NPuzzle35.zPosition = 1
}
}
I'm not sure if it's possible to synchronize animations like this: with SKAction() in several separated parts, because it's necessary to be able to select them individually.
UPDATE: I've tried to follow the action group way but I have the same animation playing on each sprite instead of a different animation synchronized for each sprite (6 different animations synchronized: 6 different sprites):
let sheet13 = Puzzle13()
let sheet19 = Puzzle19()
let sheet30 = Puzzle30()
let sheet11 = Puzzle11()
let sheet29 = Puzzle29()
let sheet35 = Puzzle35()
let imageAnims = [sheet13.Puzzle13_0000() , sheet19.Puzzle19_0000(), sheet30.Puzzle30_0000(), sheet11.Puzzle11_0000(), sheet29.Puzzle29_0000(), sheet35.Puzzle35_0000() ]
let puzzle13 = SKAction.animateWithTextures(sheet13.Puzzle13_(), timePerFrame: 0.066)
let puzzle19 = SKAction.animateWithTextures(sheet19.Puzzle19_(), timePerFrame: 0.066)
let puzzle30 = SKAction.animateWithTextures(sheet30.Puzzle30_(), timePerFrame: 0.066)
let puzzle11 = SKAction.animateWithTextures(sheet11.Puzzle11_(), timePerFrame: 0.066)
let puzzle29 = SKAction.animateWithTextures(sheet29.Puzzle29_(), timePerFrame: 0.066)
let puzzle35 = SKAction.animateWithTextures(sheet35.Puzzle35_(), timePerFrame: 0.066)
let group = SKAction.group([puzzle13,puzzle19,puzzle30,puzzle11,puzzle29,puzzle35])
for i in 0..<imageAnims.count {
let imageAnim = imageAnims[i]
let spriteAnim = SKPuzzle(texture: imageAnim)
spriteAnim.name = kanimNodeName
spriteAnim.name2 = "\(i)"
let offsetFraction = (CGFloat(i) + 1.0)/(CGFloat(imageAnims.count) + 1.0)
spriteAnim.position = CGPoint(x: ((size.width)*2) * offsetFraction, y: size.height * 1.5)
spriteAnim.zPosition = 3
spriteAnim.runAction(SKAction.repeatActionForever(group))
background.addChild(spriteAnim)
}
First of all I want to list two differents method to create your SKAction:
Starting with parallel actions by using SKAction.group:
let sprite = SKSpriteNode(imageNamed:"Spaceship")
let scale = SKAction.scaleTo(0.1, duration: 0.5)
let fade = SKAction.fadeOutWithDuration(0.5)
let group = SKAction.group([scale, fade])
sprite.runAction(group)
Another useful method can be the completion , so you can know when an SKAction was finished:
extension SKNode
{
func runAction( action: SKAction!, withKey: String!, optionalCompletion: dispatch_block_t? )
{
if let completion = optionalCompletion
{
let completionAction = SKAction.runBlock( completion )
let compositeAction = SKAction.sequence([ action, completionAction ])
runAction( compositeAction, withKey: withKey )
}
else
{
runAction( action, withKey: withKey )
}
}
}
Usage:
node.runAction(move,withKey:"swipeMove",optionalCompletion: {
// here the action is finished, do whatever you want
})
After that, about your project, I've seen many node.runAction.., you can also adopt this strategy to sinchronize your actions:
var myAction30 :SKAction!
var myAction31 :SKAction!
self.myAction30 = SKAction.repeatActionForever(puzzle30)
self.myAction31 = SKAction.repeatActionForever(puzzle31)
let group = SKAction.group([self.myAction30, self.myAction31])
self.runAction(group)
UPDATE: I've seen your update part, when you speak about "synchronize" probably you don't means the "running in parallel" actions.
So, if you want to run an action after another there is also:
self.myAction30 = SKAction.repeatActionForever(puzzle30)
self.myAction31 = SKAction.repeatActionForever(puzzle31)
let sequence = SKAction.sequence([self.myAction30, self.myAction31])
self.runAction(sequence)

runAction SKAction.sequence not working

I can't get the runAction method to work for this code block. I'm not sure what I am doing wrong or if there is a problem outside the block, but if anyone sees anything inside that is wrong, let's start there (*Edited to include entire function). All variables work and I'm getting zero compile errors, the program just appears to skip the line currentTestIndicator.runAction(SKAction.sequence(runCheck)). All print statements work except the one inside recurse, also current++ isn't getting called. I tried running the code without recurse and it still didn't work. Ideas?
func runLevelCheck(node: SKSpriteNode, _ state: SKSpriteNode, _ bluePatterns:Array<Array<String>>, _ blueSize: CGFloat, _ blueLocations: Array<CGPoint>, _ greenPatterns:Array<Array<String>>, _ greenSize: CGFloat, _ greenLocations:Array<CGPoint>, _ redPatterns:Array<Array<String>>, _ redSize: CGFloat, _ redLocations:Array<CGPoint>, _ orangePatterns:Array<Array<String>>, _ orangeSize: CGFloat, _ orangeLocations:Array<CGPoint>)->Bool {
var passLevel = true
var current = 0
let currentTestIndicator = SKSpriteNode()
currentTestIndicator.name = "indicator"
currentTestIndicator.size = CGSizeMake(0, 128)
blueLocations[current].y)
currentTestIndicator.anchorPoint = CGPoint(x: 0.0, y: 0.5)
currentTestIndicator.zPosition = 0
node.addChild(currentTestIndicator)
let patterns = [bluePatterns, greenPatterns, redPatterns, orangePatterns]
let sizes = [blueSize, greenSize, redSize, orangeSize]
let locations = [blueLocations, greenLocations, redLocations, orangeLocations]
let colors = [color("Light Blue 01"), color("Light Green 01"), color("Light Red 01"), color("Light Orange 01")]
for sets in patterns {
for rows in sets {
if rows != [] {
print("Current set: \(current)")
print(rows)
currentTestIndicator.color = colors[current]
currentTestIndicator.position = CGPoint(x: 0.0, y: locations[current][0].y)
print(currentTestIndicator.position)
print("Sizes current = \(sizes[current])")
let growRight = SKAction.resizeToWidth(sizes[current], duration: 0.2)
let recurse = SKAction.runBlock() {
recursion(state, rows, 0, 0, &passLevel)
current++
print("Changed current value to \(current)")
}
let pause = SKAction.waitForDuration(5.0)
let shrinkLeft = SKAction.resizeToWidth(0, duration: 0.2)
let runCheck = [growRight, recurse, pause, shrinkLeft]
currentTestIndicator.runAction(SKAction.sequence(runCheck))
}
}
current = 0
}
currentTestIndicator.removeFromParent()
print("Ran level check")
switch passLevel {
case true:
print("Level passed")
case false:
print("Lavel failed")
}
return passLevel
}

Resources