1
\$\begingroup\$

I am trying to make a game where the Hero can move like in Pokemon with SKActions. But the movement looks unprofessional and have delays. How can we handle to move the Hero smoothly with SKActions from tile to tile without checking every frame if up is touched?

(the Joystick sends just a signal if a direction is pressed and released)

Edit: I am using Xcode 8 and the new Tile Map function

\$\endgroup\$
0
\$\begingroup\$

I've added an example project to my Github account. You can download for reference.

With regards to the example project it's important to understand the Component pattern. SpriteKit has build-in support for this pattern through GKEntity and GKComponent. In this project the Tiles and Hero are entities and each entity has a component (VisualComponent) responsible for graphics & animation.

Additionally the project makes use of the Update Method pattern in a very limited way. Again, SpriteKit has build in support for this pattern. We make use of this pattern through the updateWithDeltaTime method in the Game class.


The approach used is the following …

The Hero entity stores the destination point in a variable called toGridPosition. The variable is set to nil when either the destination cannot be reached or the destination is reached already.

The example project uses a custom implementation for a tiled grid, but GameplayKit could be used instead (see GKGridGraph). The most important code in the Hero class is as follows:

override func updateWithDeltaTime(seconds: NSTimeInterval) {
    guard
        let vc = componentForClass(VisualComponent),
        let toGridPosition = self.toGridPosition else {
            return
    }

    let heroPos = vc.gridPosition

    // Calculcate distance between hero position and destination in world units.
    let dx = toGridPosition.x - heroPos.x
    let dy = toGridPosition.y - heroPos.y

    if dx == 0 && dy == 0 {
        // Stop movement if destination is already reached.
        stopMove()
    } else {
        // Move 1 world unit closer to destination point.
        var nextGridPosition = heroPos

        if dx > 0 {
            nextGridPosition.x += 1
        } else if dx < 0 {
            nextGridPosition.x -= 1
        } else if dy > 0 {
            nextGridPosition.y += 1
        } else if dy < 0 {
            nextGridPosition.y -= 1
        }

        if Game.sharedInstance.canMoveToGridPosition(nextGridPosition) {
            vc.moveToGridPosition(nextGridPosition)
        }
    }
}

The next move is automatically initiated as part of the update loop which is triggered by the GameScene. In the Game class the updateWithDeltaTime method is implemented as follows:

func updateWithDeltaTime(seconds: CFTimeInterval) {
    // Update game systems, state, add and remove entities, etc... here.

    hero.updateWithDeltaTime(seconds)
}

This method should be called around 60 times per second. So the next move will be triggered almost immediately once a move animation is finished.

In VisualComponent the move animation is implemented as follows:

func moveToGridPosition(gridPosition: int2) {
    if isMoving {
        return
    }

    isMoving = true

    let pos = Game.sharedInstance.positionForGridPosition(gridPosition)
    let move = SKAction.moveTo(pos, duration: 0.5)

    sprite.runAction(move) {
        self.isMoving = false            
    }        
}

A very important thing to remember (perhaps not relevant for you): never use SKShapeNodes when performance is important. Apple notes that SKSpriteNode has better performance than SKShapeNode, SKShapeNode should be used sparingly. It's easy to convert a SKShapeNode into a SKTexture however, which can be used to construct a SKSpriteNode:

extension SKShapeNode {
    var texture: SKTexture? {
        get {
            var texture: SKTexture?

            let converterView = SKView()
            if let shapeTexture = converterView.textureFromNode(self) {
                texture = shapeTexture
            }

            return texture
        }
    }
}

One final note: In the simulator the movement might still seem choppy (it certainly does for me on my MacBook Pro from 2015), but on devices the animation should be smooth using this approach.

\$\endgroup\$
  • \$\begingroup\$ @Lirf I've updated the answer with a Github example project. Hope everything is clear. I've deleted my previous comments, as the approach I initially told you is more suited for Mac OS X and tvOS apps. \$\endgroup\$ – Wolfgang Schreurs Sep 14 '16 at 4:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.