SpriteKit adventure pt1
Tags: spritekit, ios, iphone, swift
Published: 2022-04-03 13:55:47

## SpriteKit Node positioning on all screen sizes

Positioning nodes on the same exact spot can be a problem in SpriteKit. Perfect time to do an experiment.

> **Important:** This is an <em>[adventure post]</em>, where I will write a post <strong>WHILE</strong> I am working on the issue. So, all failed attempts, good and bad, will remain here. If you are here for a quick fix, just scroll to the <em>SOLUTION</em> section.
 
> If however you would like to go on this adventure with me, strap on your best Swift shoes, get that warm drink in your hand and let's see where we might be swept off to. (mind your feet)

![title adventure post image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/67crtp258rfxbb9505v4.png)

## History
So, I am making this game which is an homage to [Lunar Lander][lunar] text-based game from back in the day. And when I say "back in the day" I mean like **1969.** I wasn't even close to being alive then, I think my mother was like 5 years old...soooooo game is from beyond back in the day you might say. 😁

For some reason, first time I saw it, I was enchanted. So simple, so clear...but yet so challenging! Just to give you an idea of how old this game is, this is what the computer that was designed to play the game in **REAL-TIME** looked like. And by the way, this is 4 years after the initial LunarLander text based game.

![adventure post image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/p0bi6tsjoea0lomx9b92.png) 

---

So I decided to get to it, and create a modern version that will keep the simplicity, and have some "advanced" pixel art graphics to go with it, because nothing makes nostalgia come alive more than some good ol' pixel-art! ◻️

## Intro

Our journey begins with the working demo. Have a look at this little guys first flight below! 🚀

![adventure post image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lqg062i4j40tru5nk8fg.gif) 
⬅️ Tap the left side of the screen, it goes left
➡️ Tap right side of the screen, it goes right
⬆️ And if you tap with two fingers (position not important) it goes straight up!

Now, next thing I want to add is the *fuelGauge*, as we are in space, and we really need to manage our spending you know?! 💰

In case you are not familiar with *SpriteKit*, let me give you a quick rundown. We have one main *UIViewController* that is holding our scene. The *SKScene* is where all the gaming 🧙‍♀️magic happens, and viewController is there to present that scene to the user. Here is a quick glimpse.

![adventure post image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/46vpc03avfx3ee61ak3c.png) 

---

So I have 2 options for adding the *fuelGauge*:

- Add it in the ViewController like a regular *UIImageView* or *UISlider*
- Add it to the GameScene as an *SKSpriteNode*

Adding it to the viewController is not a problem at all. I can use a delegate and modify that image height or even use a *UISlider*.

BUT, I really don't want to use the slider since my *fuelGauge* needs to be quite custom, and honestly I **REALLY** want to use *SKCropNode* and just mask the current fuel state by moving that node up and down. Masking in SpriteKit works really well so let's use that to our advantage! We don't plan to have a lot of nodes so I think this is fine.

---

## Our journey begins!

This sounds like a breeze, let's add the node to our *GameScene*!

```swift
fuelGauge = SKSpriteNode()
fuelGauge.size = CGSize(width: 11, height: 260)
fuelGauge.position = CGPoint(x: 100, y: screenHeight - 260)
fuelGauge.texture = SKTexture(imageNamed: "fuelGauge")
addChild(fuelGauge)
```

I already added the image to my assets called "fuelGauge" so that is where the texture comes from. Let's build that on few devices and see what we have.

![adventure post image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jz2wsyjz4rksl3obpab2.png) 

---

Now that's not quite right. Our gauge seems to be way to the right, not to mention that the steps are going towards the middle quite a bit more than they should on iPhone 7 and SE2!
Oh well, let's fire up our good friend [DuckDuckGo][duckDuck] and see what we can find! 🐥🐥🕵️‍♂️

---

## The bug hunt 🐞

So I found this [post][post] on StackOverflow which mentions a similar issue. It says that we need to define the playableArea so gameScene knows what it's working with:

```swift
class GameScene: SKScene {
     let playableArea: CGRect
     //....
```
Then, use the following code in the GameScene initialiser:

```swift
override init(size: CGSize) {

    //1. Get the aspect ratio of the device
    let deviceWidth = UIScreen.mainScreen().bounds.width
    let deviceHeight = UIScreen.mainScreen().bounds.height
    let maxAspectRatio: CGFloat = deviceWidth / deviceHeight

    //3. For portrait orientation, use this*****
    let playableWidth = size.height / maxAspectRatio
    let playableMargin = (size.width - playableWidth) / 2.0
    playableArea = CGRect(x: playableMargin, y: 0, width: playableWidth, height: size.height)

    super.init(size: size)
}
```

So let me change the initialization for the *GameScene* in our main viewController and forward the size of the viewController that is creating it.

```swift
// ...
if let view = self.view as! SKView? {

let scene = GameScene(size: view.frame.size)
    // Set the scale mode to scale to fit the window
    scene.scaleMode = .aspectFit
// ...
```

Just to make sure everything kinda works, and before we do any "serious" math, let's place that *fuelGauge* smack middle of the screen:

```swift
//...
fuelGauge.position = CGPoint(x: playableArea.midX, y: playableArea.midY)
//...
```

---

Ok, let's see what we have so far!

![adventure post image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1y18pzbzx73e5urcheir.png) 

---

Alright! Now we're talkin'! It's not perfect, and everything seems to be a lot bigger, **BUT** everything is the same size **AND** in the same position! Which I really like, since we can adjust the size based on the screen and that would make so much sense.

So, now I think we can really make the fuelGauge position exactly the way we want it. For some reason I feel the top left corner makes the most sense, as it won't be blocked with your fingers which I am assuming will be on the bottom part of the screen. That way, you can always keep an eye on the fuel!

With all that in place, now we can just use the appropriate values to place our texture in the right spot:

```swift
fuelGauge = SKSpriteNode()
fuelGauge.size = CGSize(width: 11, height: 260)
fuelGauge.position = CGPoint(x: 16, y: playableArea.maxY - 160)
fuelGauge.texture = SKTexture(imageNamed: "fuelGauge")
addChild(fuelGauge)
```
Note that I am using *playableArea.maxY - 160* for the Y position. Since anchor-point for that node is in the middle, I am using 130 to move it down by the upper half, and them adding some more (30) to give it some breathing room.

![adventure post image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7wes44o0dk6jay11yvlq.png) 

---

## Solution

This is all good so far. But one thing is bothering me. Height of the *fuelGauge* is the same on both screens. I know I said that I like that initially, and I still do. But I wan't to control the size of it depending on the screen height. On smaller phones this will be almost half the screen! Which is not something I want.

Therefore let's try and make it so that with one constant we can control the width **AND** the height of the *fuelGauge*.  In my opinion taking 3rd of the screen is quite sufficient.

```swift
let fuelGaugeHeight = playableArea.maxY / 3
```
And now that we have that, what are we to do with our width which was hardcoded to 11points? Well, since our texture is 11x260, we could calculate the ratio which is **260/11 = 23**

With that in mind, our new code for the fuel texture is this:

```swift
fuelGauge = SKSpriteNode()
fuelGauge.size = CGSize(width: fuelGaugeHeight/23, height: fuelGaugeHeight)
fuelGauge.position = CGPoint(x: 16, y: playableArea.maxY - safeZoneTop - fuelGaugeHeight/1.5)
fuelGauge.texture = SKTexture(imageNamed: "fuelGauge")
addChild(fuelGauge)
```

That **fuelGaugeHeight/1.5** is the same as the  **- 160** we had before, we are just adding a bit more than the half of the height to have a bit of space.

Now, I realise that using "magic numbers" and hardcoding bunch of stuff is not recommended, but at the moment we are only tied in to that left spacing and the height ratio. I prefer to be precise and make all my screens look as similar as I can, and I am quite happy with this outcome!

![adventure post image](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mbmdjycm6p44zq5nzfrg.png) 

*HELLS YEAH!!* Now that is what a call an **effinng consistency!** If I had a mic I would drop it! 🎤👇

I love this current look, and everything seems to be in order. I suspect iPad would be a different beast, but right now this is only planned for the iPhone. Who knows, with some adjustments iPad can join in, but we'll see. 🕵️‍♂️

---

And that's it! Our *fuelGauge* has a good placement and next, I will work on the pesky *SKCropNode*, so stay tuned!

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 <a href="https://twitter.com/emin_roblack">Twitter</a>.</br>
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.
</p>


[lunar]: https://en.wikipedia.org/wiki/Lunar_Lander_(video_game_genre)
[duckDuck]: https://duckduckgo.com/
[post]: https://stackoverflow.com/questions/33707965/how-to-properly-display-a-hud-on-a-spritekit-game-across-different-resolutions
[twitter]: https://twitter.com/emin_roblack