Skscene Becomes Unresponsive While Being Idle

SKScene becomes unresponsive while being Idle

is your SceneKit scene playing ?
A scene is playing when you either have

  • scene.playing = YES
  • SCNAction instances running
  • implicit or explicit animations running

The SpriteKit scene overlay is refreshed only when the SceneKit scene is. So if the SceneKit scene does not redisplay the actions set in the overlay might not run.

A solution might be to set the playing property or to add actions to SceneKit objects instead of SpriteKit objects.

This is unrelated but try to keep away from timers. SceneKit provides several game loop callbacks that will allow you to do what you want.

view?.ispaused = true with skscene not working

the problem is that even when a scene isPaused the code still get run. Yes you are pausing the scene, yes the visuals do get run...but the visuals after the isPaused line also get run and reset the visuals that you just changed.

here is a really simple example of how this working. there are 3 boxes on the scene; the bottom box has an action that repeatedly scales up and down, and will stop when the scene is paused. the top box will pause the game when pressed and will reveal the middle box which will unpause the game.

class GameScene: SKScene {

private var test: SKSpriteNode!
private var test2: SKSpriteNode!
private var test3: SKSpriteNode!

override func didMove(to view: SKView) {

backgroundColor = .clear

test = SKSpriteNode(color: .blue, size: CGSize(width: 200, height: 200))
test.position = CGPoint(x: 0, y: 350)
addChild(test)

test2 = SKSpriteNode(color: .red, size: CGSize(width: 200, height: 200))
test2.position = CGPoint(x: 0, y: 0)
test2.alpha = 0
addChild(test2)

test3 = SKSpriteNode(color: .yellow, size: CGSize(width: 200, height: 200))
test3.position = CGPoint(x: 0, y: -350)
addChild(test3)

let scaleDown = SKAction.scale(to: 0.1, duration: 1.0)
let scaleUp = SKAction.scale(to: 1.0, duration: 1.0)
let sequence = SKAction.sequence([scaleDown, scaleUp])
let repeater = SKAction.repeatForever(sequence)
test3.run(repeater)
}

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {

if let touch = touches.first as UITouch! {

let touchLocation = touch.location(in: self)

if test.contains(touchLocation) {
print("pausing")
self.test2.alpha = 1
self.test.alpha = 0
self.isPaused = true
}

if test2.contains(touchLocation) {
print("unpausing")
self.test2.alpha = 0
self.test.alpha = 1
self.isPaused = false
}
}
}
}

Run 2 actions simultaneously? Game freezes?

The big issue you are having is the block does not wait. It just runs the code. So what is happening is you are starting the actions it hits the end of the block and because you are doing repeat forever it calls it again even though the actions are not finished. This is causing that block to be called as fast as it can.

I am not entirely sure where you want the delay but something like this should work or get you started in the right direction.

let delay = SKAction.waitForDuration(4.0) //whatever your delay is

let scaleDownGroup = SKAction.group([moveDownLeft, scaleBlock])
let scaleUpGroup = SKAction.group([moveUpLeft, scaleBlockBack])

let sequence = SKAction.sequence([scaleDownGroup, delay, scaleUpGroup])

let repeat = SKAction.repeatActionForever(sequence)

self.block1.runAction(repeat)

Hopefully that helps.

Incrementing counter in update loop?

You can use the repeatActionForever with sequence actions to make a repeating timer that runs a block every 2 seconds:

SKAction.repeatActionForever(
SKAction.sequence([SKAction.waitForDuration(2),
SKAction.runBlock({self.counter++})])
)

How best to code an Undo function in SpriteKit?

There isn't a specific UNDO technic prebuilt in sprite-kit. My advice to save memory and performance is to build a stack of the user actions. You can make something like:

public struct Stack<T> {
fileprivate var array = [T]()

public var count: Int {
return array.count
}

public var isEmpty: Bool {
return array.isEmpty
}

public mutating func push(_ element: T) {
array.append(element)
}

public mutating func pop() -> T? {
return array.popLast()
}

public var top: T? {
return array.last
}
}
enum ActionType:String {
case remove, add, moveTo, scale, rotate // add all the available user actions
}
struct UserAction {
var type: ActionType
var objectName: String
}

So, first of all you can create the LIFO (last-in first-out order) where the element you pushed last is the first one to come off with the next pop. Then you can create your custom LIFO element called in this example UserAction where you save the kind of action (remove, add, rotate..) and the name property of the object involved (for example if you have an SKSpriteNode called box1 you should implement also box1.name = "box1" just to identify your involved node.

Usage:

var undoArray = Stack(array:[])
let currentAction = UserAction.init(type: .remove, objectName: "box1")
print(" - User has \(currentAction.type.rawValue) the \(currentAction.objectName)")
undoArray.push(currentAction)

Obviously, this is only the UNDO array implementation. Your next step should be to build every single action like remove, add, rotate, to replicate the user action.

In other words to make another example when the user press the UNDO button you should "popping" an element from your stack:

// ...

if let lastEl = undoArray.pop() {
let lastUserAction = lastEl as! UserAction
switch lastUserAction.type {
case .remove: print("replicate remove action")
case .add: print("replicate add action")
case .moveTo : print("replicate moveTo action")
// ... add the other user actions
default:break
}
}


Related Topics



Leave a reply



Submit