Arie Pinto
Arie Pinto

Reputation: 1292

Finish action running on a SpriteNode without disabling touches

i have a player who's physicsBody is divided to parts (torso, legs, head, ect), now i have a button on screen that control the movement of the player, if the button is pressed to the right the player moves to the right, and that part works fine. the problem is every time the movement changes the walk() method is called and what it does is animate the player legs to look like its walking, but if the movement doesn't stop then is keeps calling the walk() without it finishing the animation, so it looks like its stuck back and forth. what i am trying to achieve is for the player to be able to walk constantly but for the walk() (animation method) to be called once, finish, then called again(as long as the button to walk is still pressed). here what i have so far:

func walk(){
    let leftFootWalk = SKAction.run {
        let action = SKAction.moveBy(x: 1, y: 0.1, duration: 0.1)
        let actionReversed = action.reversed()
        let seq = SKAction.sequence([action, actionReversed])
        self.leftUpperLeg.run(seq)
        self.leftLowerLeg.run(seq)
    }

    let rightFootWalk = SKAction.run {
        let action = SKAction.moveBy(x: 0.4, y: 0.1, duration: 0.1)
        let actionReversed = action.reversed()
        let seq = SKAction.sequence([action, actionReversed])
        self.rightUpperLeg.run(seq)
        self.rightLowerLeg.run(seq)
    }

    let group = SKAction.sequence([leftFootWalk, SKAction.wait(forDuration: 0.2), rightFootWalk])

    run(group)
}


extension GameScene: InputControlProtocol {
func directionChangedWithMagnitude(position: CGPoint) {
    if isPaused {
        return
    }

    if let fighter = self.childNode(withName: "fighter") as? SKSpriteNode, let fighterPhysicsBody = fighter.physicsBody {

        fighterPhysicsBody.velocity.dx = position.x * CGFloat(300)
        walk()

        if position.y > 0{
            fighterPhysicsBody.applyImpulse(CGVector(dx: position.x * CGFloat(1200),dy: position.y * CGFloat(1200)))
        }else{
            print("DUCK") //TODO: duck animation
        }
    }
}

Upvotes: 1

Views: 175

Answers (2)

Benny Davidovitz
Benny Davidovitz

Reputation: 1202

First of all you can use SKAction.runBlock({ self.doSomething() }) as the last action in your actions sequence instead of using completion.

About your question, you can use hasAction to determine if your SKNode is currently running any action, and you can use the key mechanism for better actions management.

See the next Q&A checking if an SKNode is running a SKAction

Upvotes: 1

Arie Pinto
Arie Pinto

Reputation: 1292

I was able to solve this, however i'm still sure there is a better approach out there so if you know it be sure to tell. here is what i did: first i added a Boolean called isWalking to know if the walking animation is on(true) or off(false) and i set it to false. then i added the if statement to call walk() only if the animation is off(false) and it sets the boolean to on(true).

func directionChangedWithMagnitude(position: CGPoint) {
    if isPaused {
        return
    }

    if let fighter = self.childNode(withName: "fighter") as? SKSpriteNode, let fighterPhysicsBody = fighter.physicsBody {

        fighterPhysicsBody.velocity.dx = position.x * CGFloat(300)
        print(fighterPhysicsBody.velocity.dx)

        if !isWalking{
            isWalking = true
            walk()
        }
    }
}

and i changed walk() to use completions blocks and set the Boolean to off(false) again

func walk(){
    let walkAction = SKAction.moveBy(x: 5, y: 5, duration: 0.05)
    let walk = SKAction.sequence([walkAction, walkAction.reversed()])

    leftUpperLeg.run(walk) {
        self.rightUpperLeg.run(walk)
    }

    leftLowerLeg.run(walk) { 
        self.rightLowerLeg.run(walk, completion: { 
            self.isWalking = false
        })
    }
}

Upvotes: 0

Related Questions