Emin Grbo, May 15 2020

SKCropNode and sizing, SpriteKit Adventures pt2

In the first post I found a nice way to add my fuelGauge in the upper left section of the screen. Now let's try to animate that fuel so it reflects our consumption.

History

If you are interested how we got here, go here to check out the First Post. If you are however up to speed, let's get to it!

Our journey begins!

Like I mentioned in the description, my idea is to add the SKCropNode so I can "animate" fuel consumption by moving it up and down. I found this post by Paul Hudson which explains it really well.

So, I will place a base gauge first, then on top if that, an SKCropNode which will crop the fuel amount.

// MARK: Fuel Gauge Base (this is basically the gauge without the bright green fuel texture)-------------------------------
let fuelGaugeBase = SKSpriteNode()
fuelGaugeBase.size = CGSize(width: fuelGaugeHeight/23, height: fuelGaugeHeight)
fuelGaugeBase.position = CGPoint(x: 16, y: playableArea.maxY - fuelGaugeHeight/1.5)
fuelGaugeBase.texture = SKTexture(imageNamed: "fuelGaugeBase")
addChild(fuelGaugeBase)
// ------------------------------------------------------------------------------------

// MARK: Fuel Gauge -------------------------------
fuelGauge = SKSpriteNode()
// We are using 0's here since its positioned in regards to the parent, or SKCropNode
fuelGauge.position = CGPoint(x: 0, y: 0)
fuelGauge.texture = SKTexture(imageNamed: "fuelGauge")
fuelGauge.size = CGSize(width: 5, height: fuelGaugeHeight * 0.97)
// ------------------------------------------------------------------------------------

// MARK: Crop Node -------------------------------
let cropNode = SKCropNode()
cropNode.position = CGPoint(x: 16, y: playableArea.maxY - fuelGaugeHeight/1.5)
cropNode.maskNode = SKSpriteNode(imageNamed: "fuelGaugeMask")
cropNode.addChild(fuelGauge)
cropNode.zPosition = 1
addChild(cropNode)
// ------------------------------------------------------------------------------------

So far so good, positioning seems to be ok and sizes are great.

Next, I want to move the fuelGauge up and down, therefore masking the fuel amount. I think I can do that by moving the base SKSpriteNode which is a child of the SKCropNode

All I need to do is change the Y position of the node. -100 should give us an ok result.

//...
fuelGauge.position = CGPoint(x: 0, y: -100)
//...

F**k me...that is not looking good at all. It does mask the gauge, but the size of the mask is not at all close the the size of the fuelGauge node we added. It just picks up the size of the sprite which we defined here:

cropNode.maskNode = SKSpriteNode(imageNamed: "fuelGauge")

Time to look around and see if I can make that SKCropNode size the same as the fuelGauge. I do have other options I can use here, but I REALLY want to crop it. Because reasons.


The Solution 🏆

I haven't found anyone with similar issues, so that only meant that I am the issue 😁 I took a short break over the weekend (🍻🍻🍻) and believe it or not, five minutes after I sat down and looked at the code again, and it hit me!

It was actually quite obvious, it just never occurred to me to try NOT using a sprite for the mask at all.

Our .maskNode is picking up the sprite size and it doesn't care that we changed all our sizes depending on the playable area. All it needs to know is the SIZE we are using now!

Let's give this one a go ⬇️

//...
cropNode.maskNode = SKSpriteNode(color: .green, size: CGSize(width: fuelGauge.size.width, height: fuelGauge.size.height * 0.97))
//...

Now THAT'S WHAT I AM TALKIN' 'BOUT! 🤜💥🤛


Let's Polish this up 🇵🇱

Now that size works, let's animate the usage of the fuel by moving the sprite inside the mask upwards. Just as an extra "thing" I will make the Lander consume more fuel while both thrusters are engaged.

First, we will declare a globalVariable to hold the Y position of the sprite.

var fuelConsumption : CGFloat = 0
.
.
.
fuelGauge.position = CGPoint(x: 0, y: fuelConsumption)

And place out consumption logic inside a newly created updateFuel() method:

func updateFuel() {

    // Determine consumption
    switch touchCount {
    case 1:
        fuelConsumption += 0.3
    case 2:
        fuelConsumption += 0.45
    default:
        fuelConsumption -= (isTouching && fuelConsumption > 0) ? 0.8 : 0.0
    }
    
    // Update sprite position
    fuelGauge.position.y = fuelConsumption
}

Which of course we will then add to the very beginning of our default update() method

override func update(_ currentTime: TimeInterval) {
    
    updateFuel()
    .
    .
    .

Done'zo Washington!

And here is the final product (for now) Our Lander can now spend fuel as well as re-fuel.

Not sure how much this would cost IRL but hey, that's the drivers problem, now mine 😉.

Also, as a kind of a dumb joke I made the fuel consumption move UP since...we are in SPACE 🛰.

This has been an experiment post, and I am really interested if you liked it...or not!
I would really appreciate if you let me know on Twitter.
Even if you just send me a message "It sucks dude", it would mean the world to me 🌎.
No hard feelings, I appreciate honesty more than you can imagine.

Tagged with: