Easier way to scale assets for various iPhone sizes? - ios

When creating apps, is there any easier way to scale images, aside from creating and loading different assets based on the detected device? It just seems kind of ridiculous to have to go through the pain of creating different images for iPhone 6+, 6, 5, 4, and also tweaking various positioning on the screen based on the device.
Also, I'm using Spritekit particle effects, and those have to be re-created too for different devices.
I know you can just have Xcode upscale iPhone 5 assets, but obviously the trade-off is image sharpness. It would be nice if you could just design everything for the largest iPhone, and have it scale down automatically for other devices. There isn't such a way, is there?

define a micro in ViewController.m file
#define IS_WIDESCREEN ( fabs( ( double )[ [ UIScreen mainScreen ] bounds ].size.height - ( double )568 ) < DBL_EPSILON )
invoke below method
now see the code below this method work fine for every iPad device if you have aspect ratio of 1024*768 but little bit squashed for iPhone5 ,5s,6,6+ but this method is the fastest way to create game for all your device because its squashed SKScene based on screen ratio.
-(void)addAspect
{
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone) {
if (IS_WIDESCREEN) {
_globalscaleX=(1024.0f*2)/(568*2);
_globalscaleY=(768.0f*2)/(320*2);
LoaderScreen = [loadingScene sceneWithSize:CGSizeMake(_yourSKView.bounds.size.width*_globalscaleX, _yourSKView.bounds.size.height*_globalscaleY)];
//LoaderScreen.scaleMode = SKSceneScaleModeAspectFit;
LoaderScreen.scaleMode = SKSceneScaleModeFill;
} else {
if([[UIScreen mainScreen] bounds].size.height==480)
{
_globalscaleX=(1024.0f*2)/(480*2);
_globalscaleY=(768.0f*2)/(320*2);
}
else
{
_globalscaleX=(1024.0f*2)/(568*2);
_globalscaleY=(768.0f*2)/(320*2);
}
LoaderScreen = [loadingScene sceneWithSize:CGSizeMake(_yourSKView.bounds.size.width*_globalscaleX, __yourSKView.bounds.size.height*_globalscaleY)];
LoaderScreen.scaleMode = SKSceneScaleModeFill;
}
} else {
_deviceType=ipad;
_globalscaleX=1.0f;
_globalscaleY=1.0f;
LoaderScreen = [loadingScene sceneWithSize:CGSizeMake(_yourSKView.bounds.size.width, _yourSKView.bounds.size.height)];
LoaderScreen.scaleMode = SKSceneScaleModeAspectFill;
}
// Present the scene.
[_yourSKView presentScene:LoaderScreen];
}
2) now second solution and best solution (quality wise) is that go two textures atlas where first texture atlas follow iPad series ratio and second 5 and 6 series because 5 and 6 follow same aspect ratio so you don't need to worry but for 4s squashed little SKScene (that can't be fixed without three texture atlas)

It is all about the Aspect Ratio for the different models.
Luckily there only two for iOS devices 4:3 or 16:9. This would be much harder for android devices as there are more different aspect ratio's.
Check here for info on that :
iOS Device Resolution
You could take the highest res images for each aspect ratio and use texturepacker to do the scaling.
This will cost some money but it's worth it.(not affiliated with the company).
Or checkout the free android stuff!:
Android Asset Studio

Related

SpriteKit - How do I handle multiple screen sizes?

My new game is finished and now I'm just testing on multiple real devices. I came across multiple issues after testing. The big issue is how to handle screen sizes. The game is how I want it to look on 6 Plus/6s Plus, but not on 6s, 6, 5s, 5, 4s, 4, or iPad.
I found these two answers but I don't know how to implement them: How to support multiple screen sizes in SpriteKit?
and SpriteKit how to get correct screen size
I really would like any type of help, because this is irritating me.
Its quite a commonly asked question.
What we usually do in SpriteKt is to give the SKScene a fixed size and let SpriteKit do the scaling for you on different devices.
So basically we have 2 ways to do it correctly
1) Set scene size to iPad (e.g 1024x768 -landscape, 768x1024 - portrait). This was the default setting in Xcode 7.
You than usually just have/show some extra background at the top/bottom (landscape) or left/right (portrait) on iPads which gets cropped on iPhones.
Examples of games that show more on iPads / crop on iPhones:
Altos Adventure, Leos Fortune, Limbo, The Line Zen, Modern Combat 5.
2) Apple changed the default scene size in xCode 8 to iPhone 6/7 (750*1334-Portait, 1337*750-Landscape). This setting will crop your game on iPads.
Examples of games that show less on iPads:
Lumino City, Robot Unicorn Attack
Choosing between those 2 options is up to you and depends what game you are making. I usually prefer to use option 1 and show more background on iPads.
For scale mode it is usually best to either use .aspectFill or .aspectFit.
You would use the Universal asset slot and/or device specific images. This way you will have a consistent experience on all devices
Spritekit scale full game to iPad
How to make SKScene have fixed width?
Hope this helps
In my case I have a portrait game, and I want to keep the height fixed while showing more or less content left/right depending on the device's screen.
For that in Xcode's Scene Editor I added and set an SKCameraNode for my scene, and set the scene size to the "widest" aspect ratio device (iPhone X ), that is 2436x1125 pixels.
Then set a custom class for the scene and override sceneDidLoad:
override func sceneDidLoad()
{
super.sceneDidLoad()
self.size.width = self.size.height * (UIScreen.main.bounds.size.width / UIScreen.main.bounds.size.height)
self.scaleMode = .aspectFit
}
If I want to preview how my game will look in the "narrowest" device (any iPad has a 1.5:1 ratio) I just temporarily change my scene's width to around 1500 pixels.
Note: SKView's contentMode changes nothing.
I was reminded of checking the screen size and changing the node sizes, I found this as an answer: how to check screen size of iphone 4 and iphone 5 programmatically in swift
All I had to add was this in my GameScene and it was called in every .swift:
extension UIScreen {
enum SizeType: CGFloat {
case Unknown = 0.0
case iPhone4 = 960.0
case iPhone5 = 1136.0
case iPhone6 = 1334.0
case iPhone6Plus = 1920.0
}
var sizeType: SizeType {
let height = nativeBounds.height
guard let sizeType = SizeType(rawValue: height) else { return .Unknown }
return sizeType
}
}
And this
if UIScreen.mainScreen().sizeType == .iPhone4 {
// Make specific layout for small devices.
}

Constraining proportions of GUI elements in Spritekit game

I apologize in advance because of huge post, but everybody who ever tried to make some kind of universal app knows that this is pretty problematic stuff, so please be easy on me...
The goal
What I am trying to achieve (shown on image above) is to use #2x assets on both iPhone 5 and 6, and maintain same look of an app. And if possible, to do all that without manually calculating scale and position properties of nodes based on detected device... So in short, how to achieve that app automatically constrain proportions of, and between elements (and scene)? Also I would like to have same look of an app on iPhone 6+ using #3x assets, but because of simplicity I've concentrated only on iPhone 5 an 6.
What I have found on the web is that some people saying that this (downsampling) is done automatically by iOS, for example they suggest this:
"Make #2x assets at the size for iPhone 6, and then iOS will do
downscaling automatically for iPhone 5".
But that's obviously not true when it comes to Spritekit scene, or I am missing something.
The problem
Even though iPhone 6 and iPhone 5 have same aspect ratio and same PPI, using the same asset won't look the same compared to the scene size (look menu sprite on 1st and 2nd image compared to the scene size) because PPI are related to pixel density, and iPhone 6 has more space (bigger diagonal, more inches) which means it has more pixels than iPhone 5. And this is where my problem comes from and I don't know what would be an efficient way to handle it.
What I have done so far
The second image is not a problem for GUI, but for a gameplay, in my case it is, because I want same look and feel on different devices. Just look first and third image.
Thanks to Skyler Lauren's suggestion I've managed to have same look of an app across all iPhone 5 devices either on 7.1 or 8.1 systems as well as on iPhone 6. So now, the problem is how to adapt this code to works with iPhone 6+ using #3x textures, as well as on iPhone 4s. Here is the solution for iPhone 5 and 6:
View controller.m
GameScene *scene = [GameScene sceneWithSize:CGSizeMake(375,677)];//fixed instead of view.bounds.size
scene.scaleMode = SKSceneScaleModeAspectFill;
So the scene always has fixed size at iPhone 6 dimensions and view size is changing according to device. I am using assets catalog for launch images and not xib file. Images are sized at recommended size - 640x960px for 4s, 640x1136px for 5, 750x1334px for 6 and 1242x2208 for 6+ model. Assets are sized for iPhone 6 so for that model there is no scaling at all.
Related to 4s model, when I am using this method described above, there are two black bars from each side...
So far I've tested this only on simulator and iPhone 6 device (what I see looks like on first image either on device or simulator).
Question
For now as I said everything works on iPhone 4s(with two black bars because of different aspect ratios), 5, 6 using #2x assets, but how make everything to work with iPhone 6+ using #3x assets ?
If I use 375x667 dimensions for the scene, then everything is positioned properly and has good proportions, but quality suffers (because of upscaling #2x)
Uniform GUI and Game Play
As far as I can tell the best way to handle a uniform GUI and game play is to set your scene size (regardless of device) and let SpriteKit scale from there.
GameScene *scene = [GameScene sceneWithSize:CGSizeMake(375,677)];//fixed instead of view.bounds.size
scene.scaleMode = SKSceneScaleModeAspectFill;
That is the points for an iPhone 6. Because SpriteKit works in points but devices display in pixels the scene size will be 750px x 1354px pixels for #2x devices and 1125px x 2031px for the iPhone 6+ (the device in pixels is actual 1080 x 1920).
How does this work with assets?
Well it works rather well for 1x and 2x assets in a .atlas folder. Again because everything is converted to points you can have button.png and button#2x.png in a texture atlas and the positioning will be the same and look the same for all iPhones.
What about #3x?
This is a better question for Apple. Apparently SpriteKit does not support #3x images in a texture atlas. There are a few question already on SO that have tried to address this.
One example...
Spritekit - not loading #3x images from SKTextureAtlas
It appears it hasn't been fixed in Xcode 6.2 either. If you are reading this and want #3x it might be worth filing a radar with Apple. One thing to note is that I didn't see anywhere in the docs claiming that texture atlases are suppose to support #3x (or even #2x for that matter) When they are supported you won't have to do any changes to your code. Just throw the #3x assets into your .atlas folders.
What can/should I do about the #3x assets?
My advice is to not worry about it and run #2x assets. SpriteKit does a decent job scaling images and there are a lot of apps out there that don't support #3x. As a fellow iPhone 6+ owner it is just something I have learned to live with at the moment. I hope that Apple supports the #3x images in the .atlas folder very soon.
Warnings
You are asking all devices to scale down with the exception of the iPhone 6 (and scaling up iPhone 6+) In most cases you shouldn't notice a big difference in your art (or performance from my testing), but as you know if you shrink images they may look slightly different. Also there is the black bar issue on the 4s which I don't have a solution for you at the moment.
Closing Points
You will get the exact same look and feel in your app across all devices if you set the scene size and set your scene to SKSceneScaleModeAspectFill however you are asking older devices to scale down. It saves a ton of time and planning with minor draw backs as far as I see it.
Hopefully that helps and the best of luck on your app.
Your main issues seem to be handling the various screen sizes in relation to your image assets and screen object coordinates.
My solution is to write your code as if you are coding for the iPhone 6 plus. Make all your images #3x size and your screen layout coordinates for the iPhone 6 screen size.
With just a bit of code I was able to get an uniform layout for the iPhone 6 plus, 6, 5 and 4 screen sizes. I have included screen shots for each one. The character image is 300x300. The 2 button images are 100x100.
static const float kIphone6PlusScaleFactorX = 1.0;
static const float kIphone6PlusScaleFactorY = 1.0;
static const float kIphone6ScaleFactorX = 0.9;
static const float kIphone6ScaleFactorY = 0.9;
static const float kIphone5ScaleFactorX = 0.772;
static const float kIphone5ScaleFactorY = 0.772;
static const float kIphone4ScaleFactorX = 0.772;
static const float kIphone4ScaleFactorY = 0.652;
#import "GameScene.h"
#implementation GameScene {
float scaleFactorX;
float scaleFactorY;
SKSpriteNode *node0;
SKSpriteNode *node1;
SKSpriteNode *node2;
SKLabelNode *label0;
}
-(void)didMoveToView:(SKView *)view {
self.backgroundColor = [SKColor blackColor];
if(view.frame.size.height == 736) {
NSLog(#"iPhone 6 plus");
scaleFactorX = kIphone6PlusScaleFactorX;
scaleFactorY = kIphone6PlusScaleFactorY;
}
if(view.frame.size.height == 667) {
NSLog(#"iPhone 6");
scaleFactorX = kIphone6ScaleFactorX;
scaleFactorY = kIphone6ScaleFactorY;
}
if(view.frame.size.height == 568) {
NSLog(#"iPhone 5");
scaleFactorX = kIphone5ScaleFactorX;
scaleFactorY = kIphone5ScaleFactorY;
}
if(view.frame.size.height == 480) {
NSLog(#"iPhone 4");
scaleFactorX = kIphone4ScaleFactorX;
scaleFactorY = kIphone4ScaleFactorY;
}
node0 = [SKSpriteNode spriteNodeWithImageNamed:#"Pic"];
node0.position = CGPointMake(self.size.width/2, self.size.height/2);
[node0 setScale:scaleFactorX];
[self addChild:node0];
node1 = [SKSpriteNode spriteNodeWithImageNamed:#"button0"];
node1.position = CGPointMake(100*scaleFactorX, 100*scaleFactorY);
[node1 setScale:scaleFactorX];
[self addChild:node1];
node2 = [SKSpriteNode spriteNodeWithImageNamed:#"button1"];
node2.position = CGPointMake(314*scaleFactorX, 100*scaleFactorY);
[node2 setScale:scaleFactorX];
[self addChild:node2];
label0 = [SKLabelNode labelNodeWithFontNamed:#"HelveticaNeue-Bold"];
label0.text = #"Big Game Menu";
label0.fontSize = 48*scaleFactorX;
label0.fontColor = [SKColor whiteColor];
label0.horizontalAlignmentMode = SKLabelHorizontalAlignmentModeCenter;
label0.verticalAlignmentMode = SKLabelVerticalAlignmentModeCenter;
label0.position = CGPointMake(207*scaleFactorX,690*scaleFactorY);
[self addChild:label0];
}
iPhone 4
iPhone 5
iPhone 6
iPhone 6+
Notice how even the text label is scaled down correctly not just by font size but also location.
For your reference, I did use the standard code in my GameViewController because I find it easier to work with a simpler version. This is the code I used to present my SKView:
- (void)viewDidLoad {
[super viewDidLoad];
SKView * skView = (SKView *)self.view;
SKScene *scene = [GameScene sceneWithSize:skView.bounds.size];
scene.scaleMode = SKSceneScaleModeAspectFill;
[skView presentScene:scene];
}

SpriteKit support different screen sizes

I want to create the game like this. It is puzzle game.
My questions is do I need to have different images for each devices? So for now as we can see Apple has 5 different mobile devices sizes:
iPhone 3.5''
iPhone 4''
iPhone 4.7''
iPhone 5.5''
iPad
So my question is if I have assets for iPad retina, then I also need 4 other the same assets with backgrounds and puzzle images for iPhone sizes? Or I can optimize it? Like for example if we create 3d game, openGL renders just needed frame and we don't need as many scene as many devices we have, just one.
Detect devices and setup your assets like this.
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
{
CGSize result = [[UIScreen mainScreen] bounds].size;
if (result.height == 480)
{
// Setup iPhone 4, 4s, iPod Touch Assets
}
else
{
// Setup iPhone 5, 5s, 6 Assets
}
}
else if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
{
//Setup iPad Assets
}
There are many factors to consider when supporting different screen sizes. A big factor to consider is whether or not you want separate non-retina and retina (#2x) versions of your assets. You could decide to just use retina images and let them scale down on non-retina devices, but it would not be optimized because you would have the full textures loaded into memory.
The next factor to consider is the size of your assets. Are you planning on making the game have larger assets on larger devices? Or are you planning on keeping the assets all the same size but giving larger devices a larger viewport? (i.e. iPhone might have scrollable view while iPad wouldn't need to scroll because it would have a larger view)
The next factor to consider is the aspect ratio of your assets. You will certainly need different assets for when you aspect ratios differ (such as typically in the case of background images, splash screens etc.)
I recommend you use Apple's Images.xcassets folder. It will allow you to organize all of your assets across all the different devices. And for more information about scene scaling on different resolutions, I suggest you read my answer here

different screen sizes with corona sdk

I am making my first iphone game using corona sdk and would like it to run on as many devices as possible (phones + tablets). However I am not sure how to deal with different screen sizes and resolutions. I developed my game for the iPhone 5 using corona simulator and it works fine on that device. When I tried it on lower resolution devices like the iPhone 4 I get 2 black rectangles on each side. I tried creating 2 different backgrounds with different resolutions and added this in my config.lua:
imageSuffix = {
["#2x"] = 2
}
However this does not seem to change anything... I am not sure what height and width I should set in content in the config.lua file and what heights and widths I should set for the backgrounds. I am sorry if these questions are stupid, I am just starting.
Thanks in advance!
It sounds like you need to fully read up on config files and dynamic scaling.
The question is a little to broad as such I suggest you read this article about "the ultimate config/modernizing the config".
Some screens are wider while others are more narrow. If we take
resolution out of the equation, its easier to visualize the screens.
Corona makes it easy to take resolution out of the picture using
Dynamic Scaling. With Dynamic Scaling, you can use a common set of
screen coordinates and Corona will automatically scale the text and
graphics for different resolution screens. It can scale upwards or
downwards depending on your starting point. It also can substitute
higher resolution images when it needs to scale up. This is all
managed by a Lua file in your project folder called config.lua.
Since available resolutions vary considerably, it’s helpful to use the
same scale for each device. It doesn’t matter if you’re on an iPhone
3GS at 320×480 or a Retina iPad at 1536×2048, the location (0,0)
represents the top-left corner and (320,480), in vertical portrait
mode, is the bottom-right corner. The screen center is (160,240).
Each point, in this case, is one pixel on a lower-resolution device
like the 3GS, which has a native screen resolution of 320×480, while
each point is four pixels on a Retina iPad. Don’t worry about the math
— Corona will handle it for you.
Source: http://coronalabs.com/blog/2012/12/04/the-ultimate-config-lua-file/
This goes through the creation of a config file that fully utilize the dynamic scaling and image scaling.
local aspectRatio = display.pixelHeight / display.pixelWidth
application = {
content = {
width = aspectRatio > 1.5 and 320 or math.ceil( 480 / aspectRatio ),
height = aspectRatio < 1.5 and 480 or math.ceil( 320 * aspectRatio ),
scale = "letterBox",
fps = 30,
imageSuffix = {
["#2"] = 1.8,
["#4"] = 3.6,
},
},
}

Adaptive Positioning Based on iOS Device

I made an iPhone game a few months ago, and am now trying to port it as a universal app to both the iPad and iPhone 5 with Cocos2D. I was wondering if there was a simple-ish way to determine where an object should be placed based on the device running the game.
I could use if statements to figure out which device the game is running on, so when I get the correct sized images for the device I could have separate positions for each object, but it seems like there would be a maths formula which would allow me to use a lot less code. Obviously something like a full screen background is very simple, because it just needs to be centred with:
[background setPosition:CGPointMake(screenSize.width/2,screenSize.height/2)];
I haven't a clue how to adapt a button that would be X = 144 & Y = 330 on the old 3.5inch, 640 by 960 resolution iPhone to an iPad or iPhone 5 resolution.
I'm willing to use a more recent version of iOS if it will make my life easier, but because I'm not using any of Apple's objects I don't know if that is possible.
Maybe this isn't even possible because the button will be different sizes for the iPhone and iPad version, but I thought I would ask.
yeah, i am usually facing the same problem,
but if it is just a static objects placement
i would have relative coordinates instead of absolute for every object
and then use screen sizes to place them correctly
so you might want to use a function like:
-(CGPoint) relativeToScreen:(CGPoint) p {
return ccp(screenSize.width * p.x, screenSize.height * p.y)
}
where 0.0 <= p.x =< 1.0 and the same for p.y
and don't forget about your anchorPoint, because the node position is based on it as well
and i hope you have discovered that cocos2d already does image choosing instead of you,
you just have to set right suffixes for your images: -hd, -ipad, -ipadhd
For iphone5 resolution, I position hud buttons relative to the screen dimensions. Very similar to what you are doing for the background. So for example, a pause button I want in the top left I would position like this:
[pauseButton setPosition:CGPointMake(0.0f + 30.0f, screenSize.height - 50.0f)];
For ipad it gets really tricky. The lazy way which I have implemented is to play around with the content scale factor and zoom everything up and have "dead" borders to compensate for the ipad's screen ratio. Not the best, but at least you can re-use all the same assets for the ipad.

Resources