1
votes

I have a fairly simple app built using cocos2d-iphone, but a strange positioning problem that I've been unable to resolve. The app uses sprite sheets, and there is a Retina and non-Retina sprite sheet within the app that use the exact same artwork (except for resolution, of course). There are other artwork within the app used for CCSprites that are both standard and -hd suffixed.

Within the app, a group of sprites are created when the app starts. These initially created CCSprites always position identically (and correctly) on Retina & non-Retina screens.

// In method called to setup sprites when app launches
// Cache & setup app sprites
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile: @"sprites.plist"];
sprites = [CCSpriteBatchNode batchNodeWithFile: @"sprites.png"];

hill = [CCSprite spriteWithSpriteFrameName: @"hill.png"];
hill.position = ccp( 160, 75 );
[sprites addChild: hill z: 1];

// ... [create more sprites in same fashion]
// NOTE: All sprites created here have correct positioning on Retina & non-Retina screens

When a user taps the screen a certain way, a method is called that creates another group of CCSprites (on- and off-screen), animating them all in. One of these sprites, hand, is always positioned identically (and correctly) on Retina & non-Retina screens. The others (a group of clouds) successfully create & animate, but their positions are correct only on Retina displays. On a non-Retina display, each cloud has incorrect starting positions (incorrect for x, y, or sometimes both), and their ending positions after animation are also wrong.

I've included the responsible code below from the on-touch method that creates the new sprites and animates them in. Again, it works as expected on a Retina display, but incorrectly on non-Retina screens. The CCSprites used are created in the same way at app-start to setup all the initial sprites in the app, which always have correct positions.

// Elsewhere, in a method called on touch
// Create clouds
cloud1 = [CCSprite spriteWithSpriteFrameName: @"cloud_1.png"];
cloud1.position = ccp(-150, 320);
cloud1.scale = 1.2f;

cloud2 = [CCSprite spriteWithSpriteFrameName: @"cloud_2.png"];
cloud2.position = ccp(-150, 335);
cloud2.scale = 1.3f;

cloud3 = [CCSprite spriteWithSpriteFrameName: @"cloud_4.png"];
cloud3.position = ccp(-150, 400);

cloud4 = [CCSprite spriteWithSpriteFrameName: @"cloud_5.png"];
cloud4.position = ccp(-150, 420);

cloud5 = [CCSprite spriteWithSpriteFrameName: @"cloud_3.png"];
cloud5.position = ccp(400, 350);

cloud6 = [CCSprite spriteWithSpriteFrameName: @"cloud_1.png"];
cloud6.position = ccp(400, 335);
cloud6.scale = 1.1f;

cloud7 = [CCSprite spriteWithSpriteFrameName: @"cloud_2.png"];
cloud7.flipY = YES;
cloud7.flipX = YES;
cloud7.position = ccp(400, 380);

// Create hand
hand = [CCSprite spriteWithSpriteFrameName:@"hand.png"];
hand.position = ccp(160, 650);

[sprites addChild: cloud1 z: 10];
[sprites addChild: cloud2 z: 9];
[sprites addChild: cloud3 z: 8];
[sprites addChild: cloud4 z: 7];
[sprites addChild: cloud5 z: 6];
[sprites addChild: cloud6 z: 10];
[sprites addChild: cloud7 z: 8];
[sprites addChild: hand z: 10];


// ACTION!!
[cloud1 runAction:[CCMoveTo actionWithDuration: 1.0f position: ccp(70, 320)]];
[cloud2 runAction:[CCMoveTo actionWithDuration: 1.0f position: ccp(60, 335)]];
[cloud3 runAction:[CCMoveTo actionWithDuration: 1.0f position: ccp(100, 400)]];
[cloud4 runAction:[CCMoveTo actionWithDuration: 1.0f position: ccp(80, 420)]];
[cloud5 runAction:[CCMoveTo actionWithDuration: 1.0f position: ccp(250, 350)]];
[cloud6 runAction:[CCMoveTo actionWithDuration: 1.0f position: ccp(250, 335)]];
[cloud7 runAction:[CCMoveTo actionWithDuration: 1.0f position: ccp(270, 380)]];
[hand runAction: handIn];

It may be worth mentioning that I see this incorrect positioning behavior in the iOS Simulator when running the app and switching between the standard iPhone and iPhone (Retina) hardware options. I have not been able to verify this occurs or does not occur on an actual non-Retina iPhone because I do not have one.

However, this is the only time I see this odd positioning behavior occur (the incorrect results obtained after user touch), and since I'm creating all sprites in exactly the same way (i.e., [CCSprite spriteWithSpriteFrameName:] and then setting position with cpp()), I would be especially grateful for any help in tracking down why this single group of sprites are always incorrect on non-Retina screens.

Edit / Update I have added a good bit of extra logging to investigate exactly what is happening with the created sprites. Here is an example of one of the clouds:

// Non-Retina
cloud1 position -- x: 70.000000 y: 320.000000
cloud1 anchor -- x: 0.500000 y: 0.500000
cloud1 size -- w: 384.000000 h: 576.000000
cloud1 origin -- x: -122.000008 y: 31.999989

// Retina
cloud1 position -- x: 70.000000 y: 320.000000
cloud1 anchor -- x: 0.500000 y: 0.500000
cloud1 size -- w: 172.800003 h: 121.800003
cloud1 origin -- x: -16.400003 y: 259.100006

I must say I am pretty baffled by this. I don't have those logs mixed up, either. The bounding box is bigger on non-Retina than it is on Retina? And those width/height values don't even match the size of the images they reference. Perhaps something strange is happening inside the spritesheets/textures? I also notice the origins do not match. Maybe that is affecting the output, as well? Very strange it's only happening to these sprites and no others.

Any thoughts & suggestions are appreciated.

4
Have you checked to see the actual image files themselves are correct? Perhaps the non retina version has some extra transparent space that is causing it to align incorrectly.Ben Trengrove
Yes, I'd thought of that. Even went so far as to remake the sprite sheets, double-checking they had same layout parameters and results. I'll check back into the source images used to make the sprite sheets themselves. EDIT: Checked all source images used to build sprite sheets (via Zwoptex); none have noticeable extra space & all standard-res images are scaled down versions of HD images.bobwaycott

4 Answers

2
votes

I can imagine two causes:

  1. the SD images are not exactly half the size of the HD images
  2. the images inside the plist are named incorrectly

Verify that your HD images have exactly twice the width and height of your SD images (or vice versa). Since you're downscaling from HD, make sure your HD image dimensions are divisible by two.

A common naming error happens with plists. Normally you would have image.png and image-hd.png. However with texture atlases this is slightly different. You should have atlas.png and atlas-hd.png files and related plist files, but the image inside the texture atlas should refer to image.png for both versions. A typical error is to add image-hd.png to the atlas-hd.png texture atlas.

Not sure if either of these issues apply. Certainly sounds like you might want to add logging to your sprite positions, and then compare Retina vs standard resolution positions. If these positions are offset exactly by half or double the other version's positions, you know you have a Retina vs Non-Retina positioning bug. If not, it's more likely that you have a bug in your positioning or movement code.

0
votes

Don't have a workable answer on this, though the problem is gone.

I once again trimmed all the images down to powers of 2, shaving off some additional space in the process. I swapped texture/spritesheet creation tools from Zwoptex to TexturePacker, and created two new sprite sheets as PVRs instead of PNGs, with new .plist files.

I also decided to upgrade cocos2d libs to the latest stable (I was previously using an older RC version prior to the 1.0.1 stable release).

The problem has disappeared without making any code changes. Strange.

0
votes

My guess is that replacing your spritesheets is what did the trick, but you didnt see any difference until the Simulator completely overwrote the original build of the App. I've had issues where images got cached in the Simulator and I had to delete the app (wiggle it, press the X) and then magically the updated images appeared.

When in doubt, test on a device. The Simulator is the cause of a lot of " high strangeness" . Not worth it.

0
votes

I just created a test app and tested.

So for devices without retina: ImageName.png - For iPhone/iPod ImageName~ipad.png -- For iPad

For devices with retina display: [email protected] - For iPhone/iPod ImageName@2x~ipad.png -- For iPad

And you can still use @2x if you iPhone high resolution image and iPad high resolution image have the same size. To load the image just use [UIImage imageNamed:@"ImageName.png"]; I just tested it on iOS simulator for iOS 5.1, 5.0 and 4.3. By the way why you should use @2x and nothing more.

The main thing because you shouldn't use the same graphics on iPhone and iPad, because iPhone and iPad has different size. And if you will use the same size the graphics will already done for you iPad retina display (if you previously use iPhone retina display). If you will images with different size, so you will use different image names for iPhone and iPad. So in this side you need just add @2x suffix. That's why you should use just @2x suffix. - these are my thoughts.