move a background endless in swift 4 - swift

Hi i'm trying to move a background endlessly, but i have a little problem when i simulate the code.
This is my code
didMove(to view: SKView) {
for i in 0...1 {
let backgroundNEW = SKSpriteNode(imageNamed: "New_Background")
backgroundNEW.size = self.size
backgroundNEW.anchorPoint = CGPoint(x: 0.5 , y: 0)
backgroundNEW.position = CGPoint(x: self.size.width , y: self.size.height * CGFloat(i))
backgroundNEW.zPosition = -1
backgroundNEW.name = "test"
addChild(backgroundNEW)
}
}
and
var lastUpdateTime : TimeInterval = 0
var deltaFrameTime : TimeInterval = 0
var amountToMovePerSecond : CGFloat = 200.0
override func update(_ currentTime: TimeInterval){
if lastUpdateTime == 0{
lastUpdateTime = currentTime
}
else {
deltaFrameTime = currentTime - lastUpdateTime
lastUpdateTime = currentTime
}
let amountToMoveBackground = amountToMovePerSecond * CGFloat(deltaFrameTime)
self.enumerateChildNodes(withName: "test") {
backgroundNEW, stop in
backgroundNEW.position.x -= amountToMoveBackground
if backgroundNEW.position.x < -self.size.height{
backgroundNEW.position.x += self.size.height*2
}
}
}
i got this simulation : https://gyazo.com/63ce327ce9bc0a14199f411ac187de25
my problem is the black background i dont know how i can replace it to do the background moving endless .

Here is my code I am using to scroll a background infinitely (assuming you are using SpriteKit?) You need two backgrounds to give it the look and feel of an infinitely scrolling background. I updated it to work for 3 backgrounds to provide an infinite scroll.
outside your didMove(toView:) method
var bg = SKSpriteNode()
var bg2 = SKSpriteNode()
var bg3 = SKSpriteNode()
var parallax = SKAction()
setting up your backgrounds inside your didMove(toView:) method
bg = SKSpriteNode(imageNamed: "texture")
bg.position = CGPoint(x: 0, y:0)
bg.zPosition = 3
bg.size = CGSize(width:Int, height:Int) //make sure to set the width to self.frame.size.width, the height can be anything
bg2 = SKSpriteNode(imageNamed: "texture")
bg2.position = CGPoint(x: self.frame.size.width, y:0)
bg2.zPosition = 3
bg2.size = CGSize(width:Int, height:Int) //make sure to set the width to self.frame.size.width, the height can be anything
bg3 = SKSpriteNode(imageNamed: "texture")
bg3.position = CGPoint(x: self.frame.size.width + bg2.position.x, y:0)
bg3.zPosition = 3
bg3.size = CGSize(width:Int, height:Int) //make sure to set the width to self.frame.size.width, the height can be anything
self.addChild(bg)
self.addChild(bg2)
self.addChild(bg3)
Now to handle the scrolling of the backgrounds
parallax = SKAction.repeatForever(SKAction.move(by: CGVector(dx: -self.frame.size.width, dy: 0), duration: 20))
//higher duration moves it slower, lower duration moves it faster
bg.run(parallax)
bg2.run(parallax)
bg3.run(parallax)
Finally, in the update(currentTime:) method that updates before each frame
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
if bg.position.x <= -self.frame.size.width {
bg.position.x = self.frame.size.width * 2
//this ensures that your backgrounds line up perfectly
}
if bg2.position.x <= -self.frame.size.width {
bg2.position.x = self.frame.size.width * 2
//this ensures that your backgrounds line up perfectly
}
if bg3.position.x <= -self.frame.size.width {
bg3.position.x = self.frame.size.width * 2
//this ensures that your backgrounds line up perfectly
}
}
If you have any questions, ask them so I can help you out.

Related

Node spacing issues on a vertical scrolling game with variable speeds in Spritekit

So I'm creating a game in Swift with Spritekit and have ran into an issue with getting my game to work. I'm still a beginner with programming so I've likely missed out on a solution to this myself.
Anyway, so the game concept is a simple arcade vertical scroller that involves a player trying to dodge platforms as it descends downward. The mechanics (so far) are a stationary player on the y axis that can move left and right along the x axis while the platforms scroll upward along with the background moving with the platforms to give a visual effect of descent. I've gotten a build working to be fully playable, but there's an issue with spawning the platforms perfectly spaced out. Here's a sketch:
Concept Image
The picture on the left what I'm trying to achieve, while the one on the right is my current and flawed method. The main issue with the one on the right, is that it uses a collision to trigger spawning which means the spawn trigger node (red line) has to be 1 pixel tall to allow for perfect spacing. If the spawn trigger node is more than 1 pixel tall, then the collision may not trigger on that the first pixel of contact and trigger the node a few pixels deep which throws off the entire spacing. Also if the spawn trigger is only 1 pixel tall, it often won't trigger unless the everything is scrolling at slow speeds.
I've tried to think of other methods to approach this but I'm at a loss. I cannot use a simple timer to spawn nodes at intervals because the speed at which the game scrolls is variable and is constantly changing by player controls. The only two other options I can think of (which I don't know how to do either) is either spawn node sets at fixed y-positions and set that on a loop, or change it so the player is actually descending downward while everything is generating around it (seems tougher and maybe unnecessary). I'm considering just rewriting my createPlatforms() method if I need to, but here's the code for that and the background anyway:
var platformGroup = Set<SKSpriteNode>()
var platformSpeed: CGFloat = 0.6 { didSet { for platforms in platformGroup { platforms.speed = platformSpeed } } }
var platformTexture: SKTexture!
var platformPhysics: SKPhysicsBody!
var platformCount = 0
var backgroundPieces: [SKSpriteNode] = [SKSpriteNode(), SKSpriteNode()]
var backgroundSpeed: CGFloat = 1.0 { didSet { for background in backgroundPieces { background.speed = backgroundSpeed } } }
var backgroundTexture: SKTexture! { didSet { for background in backgroundPieces { background.texture = backgroundTexture } } }
func createPlatforms() {
let min = CGFloat(frame.width / 12)
let max = CGFloat(frame.width / 3)
var xPosition = CGFloat.random(in: -min ... max)
if platformCount >= 20 && platformCount < 30 {
stage = 0
setTextures()
xPosition = frame.size.width * 0.125
} else if platformCount == 30 {
stage = 2
setTextures()
} else if platformCount >= 50 && platformCount < 60 {
stage = 0
setTextures()
xPosition = 184
} else if platformCount == 60 {
stage = 3
setTextures()
}
platformPhysics = SKPhysicsBody(rectangleOf: CGSize(width: platformTexture.size().width, height: platformTexture.size().height))
let platformLeft = SKSpriteNode(texture: platformTexture)
platformLeft.physicsBody = platformPhysics.copy() as? SKPhysicsBody
platformLeft.physicsBody?.isDynamic = false
platformLeft.physicsBody?.affectedByGravity = false
platformLeft.physicsBody?.collisionBitMask = 0
platformLeft.scale(to: CGSize(width: platformLeft.size.width * 3, height: platformLeft.size.height * 3))
platformLeft.zPosition = 20
platformLeft.name = "platform"
platformLeft.speed = platformSpeed
let platformRight = SKSpriteNode(texture: platformTexture)
platformRight.physicsBody = platformPhysics.copy() as? SKPhysicsBody
platformRight.physicsBody?.isDynamic = true
platformRight.physicsBody?.collisionBitMask = 0
platformRight.scale(to: CGSize(width: platformRight.size.width * 3, height: platformRight.size.height * 3))
platformRight.zPosition = 20
platformRight.name = "platform"
platformRight.speed = platformSpeed
let scoreNode = SKSpriteNode(color: UIColor.clear, size: CGSize(width: frame.width, height: 32))
scoreNode.physicsBody = SKPhysicsBody(rectangleOf: scoreNode.size)
scoreNode.physicsBody?.isDynamic = false
scoreNode.zPosition = 100
scoreNode.name = "scoreDetect"
scoreNode.speed = platformSpeed
let platformTrigger = SKSpriteNode(color: UIColor.orange, size: CGSize(width: frame.width, height: 4))
platformTrigger.physicsBody = SKPhysicsBody(rectangleOf: platformTrigger.size)
platformTrigger.physicsBody?.isDynamic = true
platformTrigger.physicsBody?.affectedByGravity = false
platformTrigger.physicsBody?.categoryBitMask = Collisions.detect
platformTrigger.physicsBody?.contactTestBitMask = Collisions.spawn
platformTrigger.physicsBody?.collisionBitMask = 0
platformTrigger.physicsBody?.usesPreciseCollisionDetection = true
platformTrigger.zPosition = 100
platformTrigger.name = "platformTrigger"
platformTrigger.speed = platformSpeed
let newNodes: Set<SKSpriteNode> = [platformLeft, platformRight, scoreNode, platformTrigger]
for node in newNodes {
platformGroup.insert(node)
}
let yPosition = spawnNode.position.y - transitionPlatform.size().height
let gapSize: CGFloat = -frame.size.width / 6
print(gapSize)
platformLeft.position = CGPoint(x: xPosition + platformLeft.size.width - gapSize, y: -yPosition)
platformRight.position = CGPoint(x: xPosition + gapSize, y: -yPosition)
scoreNode.position = CGPoint(x: frame.midX, y: platformLeft.position.y - platformLeft.size.height / 2)
platformTrigger.position = CGPoint(x: frame.midX, y: platformLeft.position.y)
print(platformLeft.position.y)
print(platformLeft.frame.midY)
let endPosition = frame.maxY + frame.midY
let moveAction = SKAction.moveBy(x: 0, y: endPosition, duration: 7)
for node in newNodes {
let moveSequence = SKAction.sequence([
moveAction,
SKAction.removeFromParent(),
SKAction.run {
self.platformGroup.remove(node)
}
])
addChild(node)
nodeArray.append(node)
node.run(moveSequence)
}
platformCount += 1
}
func startPlatforms() {
let create = SKAction.run { [unowned self] in
self.createPlatforms()
}
run(create)
}
func createBackground() {
for i in 0 ... 1 {
let background = backgroundPieces[i]
background.texture = backgroundTexture
background.anchorPoint = CGPoint(x: 0, y: 0)
background.zPosition = -5
background.size = CGSize(width: frame.size.width, height: frame.size.width * 2.5)
background.position = CGPoint(x: 0, y: background.size.height + (-background.size.height) + (-background.size.height * CGFloat(i)))
self.addChild(background)
nodeArray.append(background)
let scrollUp = SKAction.moveBy(x: 0, y: background.size.height, duration: 5)
let scrollReset = SKAction.moveBy(x: 0, y: -background.size.height, duration: 0)
let scrollLoop = SKAction.sequence([scrollUp, scrollReset])
let scrollForever = SKAction.repeatForever(scrollLoop)
background.run(scrollForever)
}
}
Does anybody have any suggestions on how I approach this or change it so it would work perfectly everytime?

Swift - SpriteKit how do I do infinite background scrolling with two unique backgrounds?

My code works perfectly fine for one common background but I am unable to figure out the required logic have it working perfectly fine if the backgrounds were unique.
override func didMove(to view: SKView)
{
createBackground()
}
func createBackground ()
{
var texture1 = SKTexture(imageNamed: "card1")
let texture2 = SKTexture(imageNamed: "card2")
for i in 0...1
{
let background = SKSpriteNode(texture: texture1)
background.anchorPoint = CGPoint.zero
background.position = CGPoint(x: 0, y: texture1.size().height * CGFloat(i))
addChild(background)
let moveDown = SKAction.moveBy(x: 0, y: -background.size.height, duration: 5)
let reset = SKAction.moveBy(x: 0, y: background.size.height, duration: 0)
let sequence = SKAction.sequence([moveDown, reset])
let forever = SKAction.repeatForever(sequence)
background.run(forever)
texture1 = texture2
}
}
}

Swift: Pause endless scrolling background (i.e. infinite loop)

In my game I would like certain things to pause when the "endGame" function is called, while other things stay running. I have been able to accomplish this for most actions by using the "paused" boolean, but I can't get it to work for my scrolling background. I know the problem has to do with how the background action is contained in a for loop, but I'm not sure how to get around this. Here is an example of my code:
class GameScene: SKScene {
var sun = SKSpriteNode()
var background = SKSpriteNode()
override func didMoveToView(view: SKView) {
sun = SKSpriteNode(imageNamed: "Sun")
sun.zPosition = -2
sun.setScale(0.8)
sun.position = CGPoint(x: self.frame.size.width * 0.632, y: self.frame.size.height * 0.90)
let rotate = SKAction.rotateByAngle(CGFloat(M_PI_2), duration: NSTimeInterval(10))
let rotateForever = SKAction.repeatActionForever(rotate)
sun.runAction(rotateForever)
addChild(sun)
let backgroundTexture = SKTexture(imageNamed: "Background")
backgroundTexture.filteringMode = .Nearest
let moveBackground = SKAction.moveByX(-backgroundTexture.size().width, y: 0, duration: NSTimeInterval(0.004 * backgroundTexture.size().width * 2.0))
let resetBackground = SKAction.moveByX(backgroundTexture.size().width, y: 0, duration: 0.0)
let moveBackgroundForever = SKAction.repeatActionForever(SKAction.sequence([moveBackground,resetBackground]))
for var i:CGFloat = 0; i < 4.0 + self.frame.size.width / ( backgroundTexture.size().width ); ++i {
background = SKSpriteNode(texture: backgroundTexture)
background.setScale(0.5)
background.zPosition = -1
background.position = CGPointMake(i * background.size.width, backgroundVert)
background.runAction(moveBackgroundForever)
addChild(background)
}
}
func endGame {
sun.paused = true
background.paused = true
}
}
In this example the "paused" boolean works perfectly for the rotating SKSpriteNode called "sun", but it doesn't work for the background. Any thoughts?
What you should do is create 1 SKNode for your background sprites, add all the background nodes to this node, then add the master node to your scene. Then, when you need to pause, you only pause the master node, and the master node will then pause all of its children
for var i:CGFloat = 0; i < 4.0 + self.frame.size.width / (backgroundTexture.size().width ); ++i {
var background = SKSpriteNode(texture: backgroundTexture)
background.setScale(0.5)
background.zPosition = -1
background.position = CGPointMake(i * background.size.width, backgroundVert)
background.runAction(moveBackgroundForever)
self.background.addChild(background)
}
addChild(background)
Looks like you are creating multiple backgrounds in the for loop. But you are pausing only one.

Nodes spawning behind background and I can't bring them to the front

I'm using a repeat action. When the app first loads the sprites do spawn in front of the background and so you can see them. But then when you restart the game from the score scene, the nodes spawn behind the background and I can't get them to come to the front. Does anyone know how I can fix this?
override init(size: CGSize) {
super.init(size: size)
//Background
for var index = 0; index < 2; ++index {
let bg = SKSpriteNode(imageNamed: "background")
bg.position = CGPoint(x: -100, y: index * Int(bg.size.height))
bg.anchorPoint = CGPointZero
bg.name = "background"
self.addChild(bg)
}
runAction(SKAction.repeatActionForever(SKAction.sequence([SKAction.runBlock(callEnemy), SKAction.waitForDuration(1.0)])))
The runAction is the code to repeat an action that doesn't seem to be working
//Player functions
foreground = SKNode()
addChild(foreground)
player = createPlayer()
foreground.addChild(player)
//Game hud
gameHud = SKNode()
addChild(gameHud)
}
func callEnemy() {
if player.physicsBody?.dynamic == true {
spawnEnemy()
}
}
func spawnEnemy() -> SKNode{
let enemy = SKSpriteNode(imageNamed: "Enemy1")
enemy.position = CGPoint(x: frame.size.width * random(min: 0, max: 1), y: 690 )
addChild(enemy)
enemy.physicsBody = SKPhysicsBody(circleOfRadius: enemy.size.width / 2)
enemy.physicsBody?.dynamic = true
enemy.physicsBody?.allowsRotation = false
enemy.physicsBody?.affectedByGravity = false
enemy.physicsBody?.velocity = CGVector(dx: enemy.physicsBody!.velocity.dx, dy: -200.0)
enemy.physicsBody?.restitution = 1.0
enemy.physicsBody?.friction = 0.0
enemy.physicsBody?.angularDamping = 0.0
enemy.physicsBody?.linearDamping = 0.0
if enemy.position.y <= CGFloat(0) {
enemy.removeFromParent()
}
return enemy
}
In order to have the nodes be on top of the background you need to change their zPosition. If you specify:
bg.zPosition = 0
and
foreground.zPosition = 1
the foreground will now be on top of the background. The zPosition specifies the location of each node on the z-axis.

Issue with scrolling background vertically with sprite kit

I am trying to scroll game background vertically and it works for some time and later background become empty. this is what I have tried:
var background = SKSpriteNode(imageNamed: "bgPlayScene")
func addBG() {
let backgroundTexture = SKTexture(imageNamed: "bgPlayScene")
let shiftBackground = SKAction.moveToY(-backgroundTexture.size().height, duration: 9)
let replaceBackground = SKAction.moveToY(backgroundTexture.size().height, duration: 0)
let movingAndReplacingBackground = SKAction.repeatActionForever(SKAction.sequence([shiftBackground,replaceBackground]))
for var i = 0; i<3; i++ {
println(i)
//defining background; giving it height and moving width
background=SKSpriteNode(texture:backgroundTexture)
background.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
background.size.width = self.frame.width
background.runAction(movingAndReplacingBackground)
self.addChild(background)
}
}
This is what I am getting: http://gyazo.com/3da3a267aeb030225fdb8c0d563276aa
I don't know what I am missing.Please let me know if there is another better way to do this.
As suggested by iAnum I got answer from that post and here is my code:
This way I add 2 backgrounds.
func addScrollingBG() {
bg1 = SKSpriteNode(imageNamed: "bgPlayScene")
bg1.anchorPoint = CGPointZero
bg1.position = CGPointMake(0, 0)
bg1.size = CGSize(width: frame.size.width, height: frame.size.height)
addChild(bg1)
bg2 = SKSpriteNode(imageNamed: "bgPlayScene")
bg2.anchorPoint = CGPointZero
bg2.position = CGPointMake(0, bg1.size.height + 1)
bg2.size = CGSize(width: frame.size.width, height: frame.size.height)
self.addChild(bg2)
}
And this is my update method:
override func update(currentTime: NSTimeInterval) {
bg1.position = CGPointMake(bg1.position.x, bg1.position.y-4)
bg2.position = CGPointMake(bg2.position.x, bg2.position.y-4)
if bg1.position.y < -bg1.size.height {
bg1.position = CGPointMake(bg1.position.x, bg2.position.y + bg2.size.height)
}
if bg2.position.y < -bg2.size.height {
bg2.position = CGPointMake(bg2.position.x, bg1.position.y + bg1.size.height)
}
}