Slow down SKSpriteNode with Impulse - ios

I have a simple game, where after my score the ball gets more speed with
ball.physicsBody?.applyImpulse(CGVector(dx: 1, dy: -1))
So now i want to slow down after he loses. I tried:
ball.pause = 0
and many other codes, that i found on the web. Can you help me?
// Sorry if this is not a advanced Question, I'm not that long in swift.

If you want to stop the ball completely, you can run this line:
ball.physicsBody?.velocity = CGVector(dx: 0, dy: 0)
This sets the velocity to nothing, effectively stopping the ball, without pausing it.
If you only want to slow it down, you can try doing something like this:
ball.physicsBody?.velocity = CGVector(dx: ball.physicsBody.velocity.dx - 5, dy: ball.physicsBody.velocity.dy + 5)
This brings the velocity closer to nothing (designed for your example), rather than completely stopping it.
Alternatively, you could apply an impulse in the opposite direction, that would slow down its movement in the original direction:
ball.physicsBody?.applyImpulse(CGVector(dx: -1, dy: 1))
Note that if you're trying to completely pause the ball (not just slow down or stop it, but pause it's actions) you would run this: ball.isPaused = true

Related

chasing enemy in an endless runner

I've been trying to code this for days now, without success.
My game is a 2D endless runner. My hero is locked onto a specific point on the X-axis. The obstacles are randomly spawned (height and distance) buildings.
I'd like to add a chasing enemy to the game. Currently for visuals only.
chasingCopNode = CopSprite.newInstance(position: CGPoint(x: -90, y: 0))
let follow = SKAction.moveTo(x: thiefNode.position.x - 50, duration: 5)
let wait = SKAction.wait(forDuration: 2)
let fallBehind = SKAction.moveTo(x: -90, duration: 4)
let chasingSequence = SKAction.sequence([follow, wait, fallBehind])
chasingCopNode.run(chasingSequence)
chasing = true
self.addChild(chasingCopNode)
I created this to move tha chasing sprite along the X-axis.
In my update function, I simply update the Y position of the enemy with the hero's Y position.
That is working, but not quite the thing I need. Since the Y positions are the same, the enemy sprite is flying sometimes (because there is no building under it).
I couldn't come up with any working ideas for the movement on the Y-axis.
I was thinking maybe add a function that calculates the needed Y impulse for the enemy to land on the next building, but I was not able to do that correctly.
thank you for your help!

Swift Game Scene alter vertically moving background with time?

I have a moving background which is 1500 x 600 pixels and constantly moves vertically down the screen using this code:
let bgTexture = SKTexture(imageNamed: "bg.png")
let moveBGanimation = SKAction.move(by: CGVector(dx: 0, dy: -bgTexture.size().height), duration: 4)
let shiftBGAnimation = SKAction.move(by: CGVector(dx: 0, dy: bgTexture.size().height), duration: 0)
let moveBGForever = SKAction.repeatForever(SKAction.sequence([moveBGanimation, shiftBGAnimation]))
var i: CGFloat = 0
while i < 3 {
bg = SKSpriteNode(texture: bgTexture)
bg.position = CGPoint(x: self.frame.midX, y: bgTexture.size().height * i)
bg.size.width = self.frame.width
bg.zPosition = -2
bg.run(moveBGForever)
self.addChild(bg)
i += 1
}
I now want a new background to come onto the screen after x amount of time to give the feel the player is moving into a different part of the game.
Could I put this code into a function and trigger it with NSTimer after say 20 seconds but change the start position of the new bg to be off screen?
The trouble with repeatForever actions is you don't know where they are at a certain moment. NSTimers are not as precise as you'd like, so using a timer may miss the right time or jump in too early depending on rendering speeds and frame rate.
I would suggest replacing your moveBGForever with a bgAnimation as a sequence of your move & shift actions. Then, when you run bgAnimation action, you run it with a completion block of { self.cycleComplete = true }. cycleComplete would be a boolean variable that indicates whether the action sequence is done or not. In your scene update method you can check if this variable is true and if it is, you can run the sequence action once again. Don't forget to reset the cycleComplete var to false.
Perhaps it sounds more complex but gives you control of whether you want to run one more cycle or not. If not, then you can change the texture and run the cycle again.
Alternatively you may leave it as it is and only change the texture(s) after making sure the sprite is outside the visible area, e.g. its Y position is > view size height.
In SpriteKit you can use wait actions with completion blocks. This is more straightforward than using a NSTimer.
So, to answer your question - when using actions for moving the sprites on-screen, you should not change the sprite's position at any time - this is what the actions do. You only need to make sure that you update the texture when the position is off-screen. When the time comes, obviously some of the sprites will be displayed, so you can't change the texture of all 3 at the same time. For that you may need a helper variable to check in your update cycle (as I suggested above) and replace the textures when the time is right (sprite Y pos is off-screen).

Moving SCNLight with SCNAction

I have a spotlight, created with the code beneath, casting shadows on all of my nodes:
spotLight.type = SCNLightTypeSpot
spotLight.spotInnerAngle = 50.0
spotLight.spotOuterAngle = 150.0
spotLight.castsShadow = true
spotLight.shadowMode = SCNShadowMode.Deferred
spotlightNode.light = spotLight
spotlightNode.eulerAngles = SCNVector3(x: GLKMathDegreesToRadians(-90), y: 0, z: 0)
spotlightNode.position = levelData.coordinatesForGridPosition(column: 0, row: playerGridRow)
spotlightNode.position.y = 1.5
rootNode.addChildNode(spotlightNode)
The scene is moving along the z axis, and the camera has an infinite animation that makes it move:
let moveAction = SCNAction.moveByX(0.0, y: 0.0, z: CGFloat(-GameVariables.segmentSize / 2), duration: 2.0)
cameraContainerNode.runAction(SCNAction.repeatActionForever(moveAction))
As the camera moves though, the light doesn't, so after a while, the whole scene is dark. I want to move the light with the camera, however if I apply to the light node the same moving animation, all the shadows start to flicker. I tried to change the SCNShadowMode to Forward and the light type to Directional, but the flickering is still there. With directional, I actually loose most of my shadows. If I create a new light node later on, it will seem that I have two "suns", which of course is impossible. The final aim is simply to have an infinite light that shines parallel to the scene from the left, casting all the shadows to the right. Any ideas?
Build a node tree to hold both spotlight and camera.
Create, say, cameraRigNode as an SCNNode with no geometry. Create cameraContainerNode and spotlightNode the same way you are now. But make them children of cameraRigNode, not the scene's root node.
Apply moveAction to cameraRigNode. Both the camera and the light will now move together.

How to scroll tiled sprites without gaps or glitches appearing inbetween in SpriteKit

I have a sideways scrolling tiled background of SkSpriteNodes in SpriteKit and occasionally thin gaps keep appearing between the tiles as they are not butting-up to eachother exactly. The code I am using is this;
func updateBackground() {
worldNode.enumerateChildNodesWithName("Background", usingBlock: { node, stop in
if let background = node as? SKSpriteNode {
let scrollAmount = CGPoint(x: -self.kBackgroundSpeed * CGFloat(self.deltaTime), y: 0)
background.position += scrollAmount
if background.position.x < -background.size.width {
background.position += CGPoint(x:background.size.width * CGFloat(self.kNumBackgroundSprites), y: 0)
}
}
})
}
What do I need to do to this code to stop the gaps from appearing?
If you're moving a bunch of nodes at the same time and by the same displacement, you should consider creating a "container" SKNode in which you add all these nodes as children and moving the container instead of each node individually.
To avoid minor errors like this one, you should consider having SpriteKit move the background for you using an SKAction. Something like this ought to do the trick:
let moveLeft = SKAction.moveByX(-backgroundTexture.size().width, y: 0, duration: 20)
let moveReset = SKAction.moveByX(backgroundTexture.size().width, y: 0, duration: 0)
let moveLoop = SKAction.sequence([moveLeft, moveReset])
let moveForever = SKAction.repeatActionForever(moveLoop)
background.runAction(moveForever)
If you have two backgrounds moving like this, you should be able to get a seamless effect fairly easily. This approach is also less CPU intensive than updating everything by hand, as you might imagine.
I wrote a Flappy Bird SpriteKit tutorial that solves just this problem; you might find it interesting reading.

SKAction with duration: 0 doesn't work - SpriteKit (Language: Swift)

I am trying to make a fairly simple running animation by changing 2 images every 0.15 seconds and moving the character slightly up, when it has the running image, and slightly down when it has the other. It would work properly if I have 0.5 seconds as duration, or even 0.1, but when I change it to 0, or even 0.0001 the character doesn't move at all. (the image changing works properly)
let movingUp = SKAction.moveTo(CGPoint(x: self.position.x, y: self.position.y + 10), duration: 0)
let waiting = SKAction.waitForDuration(0.15)
let movingDown = SKAction.moveTo(CGPoint(x: self.position.x, y: self.position.y - 10), duration: 0)
let movingSequence = SKAction.sequence([movingUp, waiting, movingDown])
self.runAction(SKAction.repeatActionForever(movingSequence))
let animateAction = SKAction.animateWithTextures(runningTexturesArray, timePerFrame: 0.15)
let repeatAction = SKAction.repeatActionForever(animateAction)
self.runAction(repeatAction)
I also tried moveBy, got the same result. I need to mention when I set the duration to 0.1 I got a strange behaviour having the character shivering up and down (quicker than it should). It has physics body applied because I want to use collide detection later, but I've set affectedByGravity to false. I was thinking that maybe the physicsWorld has some kind of air, which blocks the character from moving freely while in "vacuum" it could do that.
Thank you in advance for any answer. I'd really appreciate it!

Resources