Search code examples
swiftsprite-kit

How can I complete the action of an SKAction.move where the object being moved is removed from the view and then re-added while move is still occuring


In SpriteKit, I have a health bar floating above the player and when the enemy shoots the player the health bar gets updated to reflect the lost health. However the issue is that if the player is shot while it is in the process of moving then the health bar does not complete the move but the player still does.

here is the function wherein I remove the health bar and then add a slightly less wide health bar back in the position of the removed health bar. This function is called whenever the bullet makes contact with the player, which occasionally is while the player is moving

func updateHealthBar() {
    let lastPosition = healthBar.position.x
    let lastWidth = healthBar.frame.width
    let lostLife = healthBorder.frame.width / CGFloat(kMaxNumHealth)

    healthBar.removeFromParent()

    healthBar = SKShapeNode(rectOf: CGSize(width: lastWidth - lostLife, height: player.frame.size.height / 4))
    healthBar.position.x = lastPosition - lostLife / 2
    healthBar.position.y = healthBorder.position.y
    healthBar.lineWidth = 0
    healthBar.zPosition = 1
    healthBar.fillColor = SKColor.yellow

    addChild(healthBar)
}

I tried fixing it using a computed property to calculate the difference between the movement that already occured and the entire length of the movement to find the remaining amount needed to move and then added that difference to the move action. After a few iterations it looks something like this

var nextPosition: CGPoint!
var findDifference: Double! {
    var firstPositionDiff = 0.0
    var secondPositionDiff = 0.0
    firstPositionDiff = healthBar.position.y - player.position.y
    secondPositionDiff = nextPosition.y + player.frame.size.height / 2 + healthBar.frame.size.height - nextPosition.y
    
    return firstPositionDiff - secondPositionDiff
}

I felt I was on the right track with this solution but the math wasn't working out and only resulted in the health bar moving slightly farther and farther away from the desired end location with each move.

For the convenience of anybody looking to help or that needs more info I have also written a simplified version of the GameScene using only what I needed to recreate the problem. You can easily copy and paste it into Xcode or examine it at that link (titled GameScene.) The enemy shoots when you move closer to them by tapping the screen


Solution

  • Ok, there are many many ways to solve this, but let me state one thing first. Handling the movement of the player and of the life bar separately, in a scenario where you want them to be coordinated, looks like bad design to me. I'm still learning Swift myself, so that's just my humble opinion.

    The easiest fix is to add your life bar as a child of the player, so replace addChild(healthBar) with player.addChild(healthBar), same for healthBorder, fix the position as you wish and get rid of all healthMoveAction actions.

    Then, why removing and readding the life bar? Again, it looks like a bad practice to me. If you want to reduce the life amount in the life bar, there are many ways, you can stretch it for example. Use SKAction to decrease life, don't remove/readd the life bar.

    I posted a nice health bar in another thread. Using this, you don't need to remove and add it again when life decreases. Just call bar.update(). Or you can also take inspiration from the other answers and implement a different mechanic, which is equally valid.