DEV Community

Cover image for SpriteKit adventure pt1
emin
emin

Posted on

SpriteKit adventure pt1

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[adventure post],where I will write a postWHILEI 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 theSOLUTIONsection.

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

History

So, I am making this game which is an homage toLunar Landertext-based game from back in the day. And when I say "back in the day" I mean like1969.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 inREAL-TIMElooked like. And by the way, this is 4 years after the initial LunarLander text based game.

adventure post image


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
⬅️ 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 thefuelGauge,as we are in space, and we really need to manage our spending you know?! 💰

In case you are not familiar withSpriteKit,let me give you a quick rundown. We have one mainUIViewControllerthat is holding our scene. TheSKSceneis 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


So I have 2 options for adding thefuelGauge:

  • Add it in the ViewController like a regularUIImageVieworUISlider
  • Add it to the GameScene as anSKSpriteNode

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

BUT, I really don't want to use the slider since myfuelGaugeneeds to be quite custom, and honestly IREALLYwant to useSKCropNodeand 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 ourGameScene!

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

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


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 friendDuckDuckGoand see what we can find! 🐥🐥🕵️‍♂️


The bug hunt 🐞

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

classGameScene:SKScene{
letplayableArea:CGRect
//....
Enter fullscreen mode Exit fullscreen mode

Then, use the following code in the GameScene initialiser:

overrideinit(size:CGSize){

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

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

super.init(size:size)
}
Enter fullscreen mode Exit fullscreen mode

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

//...
ifletview=self.viewas!SKView?{

letscene=GameScene(size:view.frame.size)
// Set the scale mode to scale to fit the window
scene.scaleMode=.aspectFit
//...
Enter fullscreen mode Exit fullscreen mode

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

//...
fuelGauge.position=CGPoint(x:playableArea.midX,y:playableArea.midY)
//...
Enter fullscreen mode Exit fullscreen mode

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

adventure post image


Alright! Now we're talkin'! It's not perfect, and everything seems to be a lot bigger,BUTeverything is the same sizeANDin 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:

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)
Enter fullscreen mode Exit fullscreen mode

Note that I am usingplayableArea.maxY - 160for 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


Solution

This is all good so far. But one thing is bothering me. Height of thefuelGaugeis 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 widthANDthe height of thefuelGauge.In my opinion taking 3rd of the screen is quite sufficient.

letfuelGaugeHeight=playableArea.maxY/3
Enter fullscreen mode Exit fullscreen mode

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 is260/11 = 23

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

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)
Enter fullscreen mode Exit fullscreen mode

ThatfuelGaugeHeight/1.5is the same as the- 160we 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

HELLS YEAH!!Now that is what a call aneffinng 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! OurfuelGaugehas a good placement and next, I will work on the peskySKCropNode,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 onTwitter.
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.

Top comments(0)