SomaMan
SomaMan

Reputation: 4164

SpriteKit - correct location of sprites during SKAction

I'm writing an app with a continuously (sdieways) scrolling terrain, which is made up dynamically of images. So I create a sprite, scroll it sideways, & when it's about to show its right edge as it continues to scroll, I add a second sprite to its right, which also scrolls, etc, etc. As they disappear off the left, they're removed.

The problem is that when I add the new sprite, there's a small gap (around 4 px) between it & the previous one, as if in the time it takes to put it on screen, the scroll animation has moved a few pixels.

The scrolling class is this -

import UIKit
import SpriteKit


private let lagOffset: CGFloat = 4.0 // currently, this is the only way I can get rid of the gap between terrain blocks


class GameScene: SKScene
{
    private var terrainNumber = 2
    private var followingTerrainBlock: TerrainBlock? = .None


    override init(size: CGSize)
    {
        super.init(size: size)

        // create the first terrain block

        let terrainBlock = TerrainBlock(size: Size.terrainBlock, terrainNumber: 1)
        terrainBlock.position = CGPoint(x: size.width / 2.0 + Size.terrainBlock.width, y: size.height / 2.0)

        addChild(terrainBlock)

        followingTerrainBlock = terrainBlock

        moveTerrainBlock(terrainBlock)
    }

    required init?(coder aDecoder: NSCoder)
    {
        fatalError("init(coder:) has not been implemented")
    }


    func moveTerrainBlock(terrainBlock: TerrainBlock)
    {
        // create the next terrain block on a background thread as it's quite expensive
        // it'll be ready in plenty of time for when it's needed

        GCD.async { () -> () in
            self.followingTerrainBlock = TerrainBlock(size: Size.terrainBlock, terrainNumber: self.terrainNumber)
        }


        var targetX = size.width / 2.0
        let y = terrainBlock.position.y

        let move = SKAction.moveTo(CGPoint(x: targetX, y: y), duration: Time.terrainScroll)

        // move the terrain block to centre on the screen - at this point we add the following terrain block immediately behind it, off-screen
        // the scroll animation lasts 5 seconds before the completion closure runs, by which time self.followingTerrainBlock has been ready for ages 

        terrainBlock.runAction(move) { [weak self]() -> Void in

            if let strongSelf = self
            {
                strongSelf.terrainNumber++

                // we have to put an offset into the position of the new terrain block, or there'll be a gap between the old & new blocks

                strongSelf.followingTerrainBlock!.position = CGPoint(x: targetX + Size.terrainBlock.width - lagOffset, y: y)
                strongSelf.addChild(strongSelf.followingTerrainBlock!)

                // now we tell the new terrain block to follow the existing one

                strongSelf.moveTerrainBlock(strongSelf.followingTerrainBlock!)


                // then we continue moving the existing terrain block until it's off-screen, then remove the sprite

                targetX -= Size.terrainBlock.width - lagOffset
                let move = SKAction.moveTo(CGPoint(x: targetX, y: y), duration: Time.terrainScroll)

                terrainBlock.runAction(move) { () -> Void in
                    terrainBlock.removeFromParent()
                }
            }
        }
    }
}

What I'd like to know is how to put the following terrain blocks on screen so that they're exactly lined up with the right edge of the previous one. If I stop the animation & add one, the position is correct without the offset, so there's nothing wrong with the position calculation.

Upvotes: 2

Views: 70

Answers (1)

Chameleon
Chameleon

Reputation: 1608

As you create each block, create a constraint (with constant = 0) attaching it to previous block...etc for each block there after.

I believe that as each block moves off the view the constraint will dealloc as well, but you might have to consider this part too.

Upvotes: 1

Related Questions