SKAction continue working - ios

I'm using the following to pause my game
self.scene?.paused = true
When the scene is paused, however, all SKActions stop. What can I do to allow some of the actions to continue to work?

Add certain nodes to different SKNode so instead you can only pause the the layer (SKNode) that you wish to pause. It would look something like this:
let gameLayer = SKNode()
let pauseLayer = SKNode()
Now when you want to add a child to the scene, instead add it to a layer:
gameLayer.addChild(gameSceneNode)
pauseLayer.addChild(pauseSceneNode)
Don't forget to add the layers to the scene too
addChild(gameLayer)
addChild(pauseLayer)
To pause a layer write this:
Swift 3
gameLayer.isPaused = true
Swift 2
gameLayer.paused = true
Note that in this example, all the nodes on the gameLayer will be paused, however everything on the pauseLayer will not.

You need to design your node tree in a way where you can pause certain nodes (the gameplay nodes, for example), and not pause others (the pause menu nodes, for example). When you set the paused property on a node, it applies to all of its children as well.
An example node hierarchy:
GameScene
GameplayNode
Character
Enemy
Enemy
PauseMenu
PlayButton
VolumeButton
If you want to animate your PlayButton while the game is paused, you can set the GameplayNode.paused to true, and still have working SKAction's for your pause menu.

Related

How can I disable a SpriteKit node touch event and then enable it after 25 seconds to be touched and moved to next scene?

How can I disable a SpriteKit node touch event and then enable it after 25 seconds so that when the node is touched it will take the user to the next scene?
I am setting up a GameScene that plays an audio file for 25 seconds and then after that I want the user to be able to click on a SpriteKit Node that will take the user to the next Scene. The problem is the node cannot be hidden. It needs to be visible and but disable and then visible and enabled for touch after 25 seconds.
if nextButton.contains(location) {
if nextButton.isHidden == true {
nextButton.isUserInteractionEnabled = false
} else {
goToScene(scene: getNextScene()!)
}
I have used this code for when the SPriteKit Node has been able to be hidden, but this time the Node must be visible the whole time.
From the looks of this, your GameScene is what handles the touch events, so when you are playing your audio, you should do something like this:
self.userInteractionEnabled = false
let audio = SKAudioNode()
audio.run{
SKAction.group([SKAction.play,
SKAction.sequence([
SKAction.wait(forDuration:25),
SKAction.run({self.userInteractionEnabled = true})
])
)
}
self.addChild(audio)
If you are using playSound action
self.userInteractionEnabled = false
let audio = SKSpriteNode()
audio.run{
SKAction.sequence([
SKAction.playSoundFileNamed("mySound",true),
SKAction.run({self.userInteractionEnabled = true})
])
}
Basically what this does is sets a delay action to enable touch after the sound finishes.

Designing complex objects with SpriteKit Scene Editor

Near as I can tell, there isn’t a way to add physics joints in the scene editor. Is that right?
Consider a simple person object with a body, child legs and arms w/ pin joints. If I want to design this person in the scene editor and then programmatically add him to a scene, I’m not getting very far. I’m able to find the nodes in the scene, remove them from their parent and add them as a child at a new position in my scene, but I still have to specify all their joints manually.
Thoughts?
Here is my solution. I'm still hoping there is a way to create physics joints with the scene editor but I haven't found it so...
Step 1) Add all the child nodes to the scene, ensuring that objects are grouped by parent.
Step 2) Define a swift class for your complex node.
class MyNode : SKSpriteNode {
func spawn(parentNode: SKNode, position: CGPoint) {
parentNode.addChild(self) // before physics joints
let arm = self.childNode(withName:"arm")
// note if you didn't add physics bodies in scene file
// do that first
let shoulders = SKPhysicsJointPin.joint(withBodyA:self.physicsBody!, bodyB: arm.physicsBody!, anchor: CGPoint(x:position.x,y:position.y-1))
scene!.physicsWorld.add(shoulders)
// feature of pulling a child from a scene, it's always paused by default.
self.isPaused = false
}
}
Set the class for your body node in your scene.
Step 3) Transfer your node to your game scene at init time.
let tmpScene = SKScene.init(fileNamed: "MyNode.sks")
var myNode = tmpScene.childNamed(withName:"myNode") as! MyNode
myNode.removeFromParent()
myNode.spawn(world, position) // or your own parent and position as needed for your scene

Spritekit: Pausing parent node does not pause child node

I am creating my first game in SpriteKit. I am trying to pause my GameScene and then run some animations via SpriteKit. The issue was that if I pause the scene using self.view?.isPaused = true everything stops. After searching around I found that the way to do this is to add another node to the scene and make everything else that you want to pause, child nodes of that node. I am trying to do that but haven't been successful. This is the code that I have
var worldNode:SKNode?
override func didMove(to view: SKView) {
worldNode = SKNode()
self.addChild(self.worldNode!)
let spriteNode:SKSpriteNode = SKSpriteNode(imageNamed: "red_ball")
spriteNode.size = CGSize(width: 40, height: 40)
spriteNode.physicsBody = SKPhysicsBody(circleOfRadius: 15)
spriteNode.physicsBody?.isDynamic = true
self.worldNode?.addChild(spriteNode)
spriteNode.physicsBody?.applyImpulse(CGVector(dx: 10, dy: 10))
self.worldNode?.isPaused = true
//self.view?.isPaused = true
}
when I run this I see the ball moving on the screen which should not happen according to my understanding, since I am pausing the worldNode and the spriteNode is child node of that. If I use self.view?.isPaused = true to pause the scene, then the ball is stationary as expected. Am I missing something ? how can I add nodes to the worldNode and then just pause only those nodes by pausing worldNode. Thanks !
Well one way to pause nodes moved by impulses and forces is certainly to pause the scene:
self.isPaused = true
but I would say that you want to have other nodes unpaused (eg. HUD elements) so you added your game elements into world node.
So one way to pause physics along with pausing the node is to pause the complete physics world, like this:
self.physicsWorld.speed = 0

Unexpected physicsBody in SpriteKit scene

I'm implementing a mass-spring system (many small physics bodies joined together with SKPhysicsJointSpring instances) with SpriteKit. Some of the particles would get snagged while traversing the center of the scene.
There seems to be a small, static body in the middle of the scene and I don't know why it's there.
Here's an easy way to see what I'm talking about:
In XCode 8, create a brand new project with the "Game" template.
In GameViewController.viewDidLoad(), add view.showsPhysics = true
If you run the project, you should see a little dot in the middle, which is the errant body:
Anyone know how to get rid of it?
Edit: I tried to manually create the scene object:
In GameViewController.viewDidLoad(), I replaced this:
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "GameScene") {
view.presentScene(scene)
}
with this:
let scene = GameScene(size: view.frame.size)
scene.anchorPoint = CGPoint(x: 0.5, y: 0.5)
view.presentScene(scene)
but that didn't fix it.
Anyways, I decided to make an answer because comments are not suitable due to lot of info I want to share. Also my answer, sadly, doesn't answer the question but it gives some useful info about this unidentified, obviously capable of flying (physics body) object :)
So this is the code how to grab it (and modify it???):
//you can use self.frame here...I just copied Alessandro's code
self.physicsWorld.enumerateBodies(in:(label?.frame)!) { body, stop in
if let node = body.node {
print("Type of this node: \(type(of:node))")
print("Frame of this node: \(node.frame))")
}else{
print("This body's node property is nil")
body.affectedByGravity = true
body.isDynamic = true
body.applyImpulse(CGVector(dx: 0.003, dy: 0.0003))
}
print("Area covered by this node physicsBody: \(body.area)")
}
So if you put a break point inside of that else statement, you can scan this physics body completely and get all the info about it, like that its node property is set to nil or that its isDynamic property is set to false. But you can change that, and like in my code, set for example isDynamics to true. This makes it moveable. So if you apply some forces to it, it will move.
Still, like I said in comments, I don't have an idea why it is there and what it represents or what is its purpose.
Also, for those who are wondering how it is possible that one physics body doesn't have a node associated with it ( body.node equals nil) but is still visible on screen when showsPhysics is set to true, there is a reasonable explanation. Physics world is separated from the node tree. So we can remove a sprite from a node tree, but that doesn't mean that its physics body will be removed instantly. It may happen that physics engine haven't finished simulation... So you probably wonder, how this might happen?
Let say you have three SKSpriteNode objects intersecting at the same time (say A contacts B and A contacts C at the same time). SpriteKit can process only one contact at time. And say that you are removing A from a parent when it is contacting with B. Then, there is a contact between A and C also, so didBegin:(_ contact) will be called twice. And if you remove A from its parent in first didBegin(_ contact) call, in the next didBegin(_ contact) call, bodyA.node will be nil (bodyA is a physics body of sprite A), but its physics body will remain visible until engine finishes what needed. This is because node tree and a physics world are separated.
About the "hello world" xCode game template , it seems a little physicsBody associated to the GameScene node.
With some code I've found this:
class GameScene: SKScene {
private var label : SKLabelNode?
private var spinnyNode : SKShapeNode?
override func didMove(to view: SKView) {
...
// End part of this function:
if let b = physicsWorld.body(in: (label?.frame)!) {
if let node = b.node {
print("Type of this node: \(type(of:node))")
print("Frame of this node: \(node.frame))")
}
print("Area covered by this node physicsBody: \(b.area)")
}
}
}
With a breakpoint to the last brace, you can see two bodies (maybe an array of bodies), one of them is the physicsBody to the left debugArea (array with index 1) with the same hex address as my body b in my code : 0x79794f90, a little rectangle body with area 4.444
Printing description of ((PKPhysicsBody *)0x79794f90):
<SKPhysicsBody> type:<Rectangle> representedObject:[<SKScene> name:'(null)' frame:{{-375, -667}, {750, 1334}} anchor:{0.5, 0.5}]
(lldb)
I had a similar problem. I have a game with two sprite nodes joined together (SKPhysicsJointFixed.joint) moving around an SKEditor created scene.
As per my design, this node-pair would impact a third sprite node and be propelled smoothly away from the third sprite node, EXCEPT when the impact was in the center of the scene. For the center of the scene impact, the node-pair would compress together while be propelled away from the third sprite node, presenting a poor graphical image.
After significant time debugging my code, I found this post. Kudos for the explanations and code. I can’t answer the “why” question but for the “particles would get snagged while traversing the center of the scene” question my suggested solution is to clear the collisionBitMask instead of moving the body.
BTW categoryBitMask is 0 when loaded.
//moves the problem offscreen unless it hits another node
//body.affectedByGravity = true
//body.isDynamic = true
//body.applyImpulse(CGVector(dx: 0.003, dy: 0.0003))
//
// collisionBitMask loads with 4294967295 = default value to collide with all categories
//
body.collisionBitMask = 0

Set A bool to true for two seconds then change back to false

As the question states, in spritekit I am trying to find a way to change my value of a bool from false, too true for x amount of seconds then change it back to false.
the logic is i have this var, isStunned, when my player comes in contact with x sprite i call it and my joystick is disabled since the player is "stunned"
if (controlButtonDown) { // If i pressed joystick
if (!isStunned) { // IF i am not stunned
//move player
When my player touches the glue, i set isStunned to yes and the joystick is disabled, however I am wondering of a way to only disable my joystick or not move my sprite for x amount of seconds. I know of SKAction and the wait sequence, but those are actions and I dont think they will apply here. Any help is appreciated.
Thank you
Since you ask specifically with a spritekit tag.
Don't use NSTimer or actions to work with time in a game. Instead use the update function. If you're running at 60fps that means the update function will be called 120 times for two seconds. Using the frames instead of seconds to keep your game updated will avoid the gameplay being changed by lag. Imagine for instance that the players game lags a little, in other words runs slower. 2 seconds is still 2 seconds regardless of game lag, so now he is affected less by the glue than a person who has no lag. So...
First off you should have a variable keeping track of in game time: int _time;
In every update cycle you add one to time: _time++;
You should have a variable keeping track of when the user touched the glue: int _glueTouchedAtTime;
When the user clicks glue you set: _glueTouchedAtTime = time;
You should have a constant defining how long the glue is active: #define GLUE_TIME 120;
When you initiate the game set _glueTouchedAtTime to: _glueTouchedAtTime = -GLUE_TIME; To prevent the glue from being on when (_time == 0)
Testing if the user has touched glue now works like this:
if(_glueTouchedAtTime+GLUE_TIME>time) {
// Glue is active
} else {
// Glue is not active
}
Different glues for different sprites
To have different glues for different sprites I would suggest first doing a general game object (GameObject) as a subclass of either SKNode or SKSpriteNode depending on your needs. Then create a subclass of GameObject called GameObjectGluable. This should have a property called: #property int gluedAtTime;
You glue the glueable game object by: aGameObjectGluable.gluedAtTime = time;
Now you can test:
if(aGameObjectGluable.gluedAtTime +GLUE_TIME>time) {
// Game Object is glued
} else {
// Game object is not glued
}
Set your value-of-interest to true and then fire a NSTimer after two seconds to set it back to false.
For an explanation of how you might use NSTimer, see this SO thread: How do I use NSTimer?
You can use SKAction also. The runBlock method let's you execute blocks of code as actions.
SKAction *wait = [SKAction waitForDuration:2.0];
SKAction *reenableJoystick = [SKAction runBlock:^{
joystick.userInteractionEnabled = TRUE;
}];
SKAction *sequence = [SKAction sequence:#[wait, reenableJoystick]];
[self runAction:sequence];

Resources