Pausing SKSpriteNode (Swift, SpriteKite) - ios

I need to pause the balls for my game. I want them to stop in place but they are moving by impulse and if I make them non-dynamic to stop them, the impulse goes away. I'm trying to pause them and un-pause them, and they still keep going in the same direction. I tried
ball.paused = true
but that didn't work. Anyone know?

Instead of freezing the nodes, I froze the scene with
scene?.physicsWorld.speed = 0
because that freezes all of the nodes and not my time or score integers which is exactly what I needed.

Here is how I solved it in my game:
var ballVelocity = CGVector()
func pauseAction(ball: SKSpriteNode){
if ball.dynamic {
//Ball is moving. Save the current velocity of ball.
ballVelocity = ball.velocity
//Stop the ball
ball.physicsBody.dynamic = false
}else{
//Ball is paused.
//Resume ball movement.
ball.physicsBody.dynamic = true
//Add the saved velocity.
ball.velocity = ballVelocity
}
}

You can use ball.physicsBody.velocity = CGVectorMake(0, 0); but keep in mind that if the ball is affected by gravity, it will still drop.

Related

How to make a Sprite an obstacle for another Sprite?

I am making a game with sprites that move and obstacles to learn SpriteKit. I want the sprites that move to collide with the obstacles and bounce off of them but I want the obstacles to stay fixed. How do I do this? I have tried the following with no success:
Setting obstacle.physicsBody?.isDynamic = true. This made the sprites go through the obstacle.
Fixing the movement and rotation of the object with SKConstraint. When I do this they just go through each other.
Setting the mass of the body to be really high as follows obstacle.physicsBody?.mass = CGPoint.maxFiniteMagnitude but this freezes the game. When I set it really high it doesn't seem to do anything.
Setting obstacle?.physicsBody.velocity = CGVector(dx: 0, dy: 0) when the objects collide. I know that the contact.bodyA and contact.bodyB are passed by value and not reference so I loop through an array with the obstacles and set the velocity this way. The obstacles are still pushed by the other sprites.
Update:
- Setting obstacle.physicsBody?.collisionBitMask = PhysicsCategory.none so the sprite collides with the obstacle but not the other way around.
The object is setup as follows, with fish being the other sprite:
obstacle.position = location
obstacle.physicsBody = SKPhysicsBody(rectangleOf: obstacle.size)
obstacle.physicsBody?.isDynamic = true
obstacle.physicsBody?.categoryBitMask = PhysicsCategory.obstacle
obstacle.physicsBody?.contactTestBitMask = PhysicsCategory.fish
obstacle.physicsBody?.collisionBitMask = PhysicsCategory.fish
obstacle.physicsBody?.usesPreciseCollisionDetection = true
self.obstacles.append(obstacle)
super.addChild(obstacle)
Please let me know if there is something I am doing wrong / misunderstanding. Thanks.
Found the answer here. One of the objects needs to have .physicsBody?.isDynamic = true. In this case I set the obstacle to false so it is stationary.

SpriteKit contact detection has slight delay

I'm creating a small game where the ball needs to bounce on the upper, left and right wall, while it should stop as soon as it touches the floor.
I already have a working contact-detection code and delegate that is called in the correct situation and with the correct timing.
Creating a small circle in the collision point found in the func didBegin(_ contact: SKPhysicsContact) creates the node in the correct position.
In the same method I call ball.physicsBody?.velocity = .zero and ball.position = contact.contactPoint and the ball correctly stops. The problem is that sometimes the ball stops a frame too late, after it's already bounced off the floor.
The ball has a restitution of 1 because I would like it to bounce without slowing down, except when touching the floor.
I have already tried to set the position to zero, the velocity to zero, its isDynamic attribute to false. Each of these tries does what it's supposed to do, but sometimes it happens a frame too late.
Both the ball and the floor have the usesPreciseCollisionDetection property set to true.
If I try to add some artificial delay, the contact method is called again (breaking part of the gameplay).
Here is the code of my ball node:
class Ball: SKSpriteNode {
convenience init() {
self.init(imageNamed: "earth")
physicsBody = SKPhysicsBody(circleOfRadius: 12.5)
physicsBody?.usesPreciseCollisionDetection = true
physicsBody?.categoryBitMask = Masks.ball
physicsBody?.contactTestBitMask = Masks.floor
physicsBody?.allowsRotation = false
physicsBody?.restitution = 1
physicsBody?.friction = 0
physicsBody?.angularDamping = 0
physicsBody?.linearDamping = 0
}
}
and here is the code for my didBegin method:
func didBegin(_ contact: SKPhysicsContact) {
ball.physicsBody?.velocity = .zero
ball.position = contact.contactPoint
}
I would expect the ball to stop while still touching the floor, and not a frame after it's already bounced off.

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

Swift Collision Not Working After SKReferenceNode Scale

I have a weird problem where my SKReferenceNode does not collide properly after being scaled up. It collides well in the center, but it ignores collisions and contacts on the edges.
Here is the first photo of the scene. The SKReferenceNode was scaled up significantly, and as seen in this photo, does not collide correctly on the edges. The PhysicsBody appears to be correct (with showPhysics on), yet the ball refuses to collide.
The SKReferenceNode is using an alpha collision mask, because I need to change it to a larger sprite in the future to do animations and such. Additionally, non-scaled objects work completely fine. Finally, after the ball collides with the center, which does work, and the block is reset, collisions start working as expected again. The code to fix it is probably in the reset function, but I reset everything before the level is loaded, so this wouldn't make sense. Here is a part of my reset code:
func reset() {
physicsWorld.gravity = CGVectorMake(0, -9.8)
for grav in gravityBlock {
grav.reset()
}
gravity = -9.8
//Resets blocks
for blocks in destroyedBlocks { //the blocks are stored in destroyedBlocks when collided with
blocks.reset()
}
destroyedBlocks = []
/*Irrelevant code removed*/
}
Here's my blocks.reset() function:
override func reset() {
super.reset()
self.physicsBody?.categoryBitMask = 1
removeAllActions()
self.texture = self.text
shadow.hidden = false
self.alpha = 0
shadow.alpha = 0
let appear = SKAction(named: "Appear")!
self.runAction(appear)
shadow.runAction(SKAction.fadeInWithDuration(0.25))
}
Here is super.reset()
func reset() {
self.hidden = false
state = .NotBroken
}
Thanks so much!
When you scale the sprite you are just changing it's visual representation. The Physics Body stays the same. That's why you are getting the "strange" during the collisions.
You can see that showing the physics uses in your scene, just open GameViewController and add this line
skView.showsPhysics = true
inside viewDidLoad, just after this line
skView.showsNodeCount = true
Then run again your game, the physics boundaries now will be highlighted.

Any way to speed up gameplay gradually in Swift?

I'm currently working on a game using Spritekit. The game has objects which spawn at the top of the screen and fall towards the player character, and the game ends when the player character collides with any of the objects. I am trying to find a way to gradually speed up gameplay over time to make the game more difficult (i.e. objects fall at normal speed when the game begins, after 5 seconds speed up 50%, after 5 more seconds speed up another 50%, ad infinitum.)
Would I need to use NSTimer to make a countdown to increase the gravity applied to the falling objects? Sorry if this is a basic thing, I'm kind of new to programming.
Thanks, Jake
EDIT:
My spawn method for enemies-
let spawn = SKAction.runBlock({() in self.spawnEnemy()})
let delay = SKAction.waitForDuration(NSTimeInterval(2.0))
let spawnThenDelay = SKAction.sequence([spawn, delay])
let spawnThenDelayForever = SKAction.repeatActionForever(spawnThenDelay)
self.runAction(spawnThenDelayForever)
And my method for making the enemies fall-
func spawnEnemy() {
let enemy = SKNode()
let x = arc4random()
fallSprite.physicsBody = SKPhysicsBody(rectangleOfSize: fallSprite.size)
fallSprite.physicsBody.dynamic = true
self.physicsWorld.gravity = CGVectorMake(0.0, -0.50)
enemy.addChild(fallSprite)
}
In spawnEnemy(), you set self.physicsWorld.gravity. Move this line to your update: method.
If you are not keeping track of the game's duration right now, you will want to implement that. You can use the parameter of the update: method to accomplish this.
You can then use the game duration to change the gravity.
For example,
override func update(currentTime: CFTimeInterval) {
if gameState == Playing{
//update "duration" using "currentTime"
self.physicsWorld.physicsBody = CGVectorMake(0.0, -0.50 * (duration / 10.0))
}
}
10.0 can be changed depending on how fast you want the gravity to increase. A higher number makes it change less drastically, and a smaller number makes the gravity increase quite rapidly.
Hopefully this answers your question.

Resources