iPhone or cocos2d bug in handling retina display touches? - iphone

I was trying to handle retina display touches in my cocos2d 1.01rc iPhone app and found that to do so I have to multiply the touch point by the scale factor (see code below) even if I set to retina display in my AppDelegate (see code at the end of page). I am a bit confused as I would have expected that the locationInView funciton would have retrieved the retina display touch and not the "standard" 480x640 resolution touch. My guess is that this is due to the fact that locationInView comes from the ios library and not from cocos2d and the retina display setup in the cocos2d App delegate does not get propagated till the ios level. Strange. I post the code below and the output for clarity but would appreciate if you had similar problems and if I should consider this "bug?" as a warning bell on something else that might be lost between cocos2d and ios sdk. I might just have been stupid an not found the proper documentation page.
That's the code I am running on ccTouchesEnded event:
-(void) ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
for(int i=0; i<[[touches allObjects] count]; i++){
UITouch *touch = [[touches allObjects] objectAtIndex:i];
CGPoint point = [touch locationInView: [touch view]];
CCLOG(#"Before x:%f y:%f", point.x, point.y);
point = [[CCDirector sharedDirector] convertToGL: point];
float factor = CC_CONTENT_SCALE_FACTOR();
CCLOG(#"factor %f:", factor);
CCLOG(#"After x:%f y:%f", point.x , point.y );
CCLOG(#"After x:%f y:%f", point.x * factor, point.y * factor);
...
}
Output:
Before x:314.000000 y:3.000000
factor 2.000000:
After x:314.000000 y:477.000000
After x:628.000000 y:954.000000
The app is running under retina display on an iPod touch 4th generation, here are some of the initialization data:
cocos2d: cocos2d v1.0.1
cocos2d: GL_VENDOR: Imagination Technologies
cocos2d: GL_RENDERER: PowerVR SGX 535
cocos2d: GL_VERSION: OpenGL ES-CM 1.1 IMGSGX535-63.14.2
cocos2d: GL_MAX_TEXTURE_SIZE: 2048
cocos2d: GL_MAX_MODELVIEW_STACK_DEPTH: 16
cocos2d: GL_MAX_SAMPLES: 4
cocos2d: GL supports PVRTC: YES
cocos2d: GL supports BGRA8888 textures: YES
cocos2d: GL supports NPOT textures: YES
cocos2d: OS version: 5.0.1 (0x05000100)
cocos2d: surface size: 640x960
I checked on google and stackoverflow and it did seem to me that this was a know bug one year ago, I am just not 100% sure if this has been fixed in the rc1.01 version or if I am doing something stupid in my AppDelegate:
- (void) applicationDidFinishLaunching:(UIApplication*)application
{
//added
CC_DIRECTOR_INIT();
// Init the window
// window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Try to use CADisplayLink director
// if it fails (SDK < 3.1) use the default director
if( ! [CCDirector setDirectorType:kCCDirectorTypeDisplayLink] )
[CCDirector setDirectorType:kCCDirectorTypeDefault];
CCDirector *director = [CCDirector sharedDirector];
[director setDeviceOrientation:kCCDeviceOrientationPortrait];
// Init the View Controller
// viewController = [[RootViewController alloc] initWithNibName:nil bundle:nil];
// viewController.wantsFullScreenLayout = YES;
//
// Create the EAGLView manually
// 1. Create a RGB565 format. Alternative: RGBA8
// 2. depth format of 0 bit. Use 16 or 24 bit for 3d effects, like CCPageTurnTransition
//
//
EAGLView *glView = [director openGLView];
[glView setMultipleTouchEnabled:YES];
// attach the openglView to the director
[director setOpenGLView:glView];
// // Enables High Res mode (Retina Display) on iPhone 4 and maintains low res on all other devices
if( ! [director enableRetinaDisplay:YES] )
CCLOG(#"Retina Display Not supported");
Any ideas? Will this affect also other aspects of my code? It does seem to concern only the touch detection and for this the fix of multiplying by the scale factor seems alright, and I did seem not to have problem when I access the relative position of the sprite objects. I will do some more testing for this and keep this in mind.
Thanks!

This is by design.
You have to understand that iOS devices report touch locations in points. Both Retina and non-Retina devices have a point resolution of 480x320 points. On Retina devices one point consists of 4 pixels, whereas on all other devices a point equals a pixel on the screen.
According to the event handling guide touches on Retina devices can be reported as .5 locations (read the first Notes box), giving you full Retina touch resolution. If you don't, then maybe that resolution gets lost during the conversions (check the original UITouch location). Either way, Retina resolution for touches is way too accurate to make any use of, 480x320 is plenty for touches.

In iOS (and Cocos) touches and a lot of things (like views positions/sizes) are not handled in pixels, but points.
In non-retina screens, a point is exactly one pixel, but in retina displays, a point is two pixels wide and two pixels tall.
This means that all your touches and other coordinates will always be in the range of a standard display (320 pixels wide and 480 pixels tall in portrait). This is great because you don't have to worry about the kind of screen, and your code is cleaner.
Both iOS and Cocos manage high resolution pretty much under the hood, so images loaded will be at high resolution, but you won't have to worry about screens with more pixels in your code.
(If for some specific reason you want to use pixels instead of points, then what you are doing is right, multiply the point by the content size. The idea behind what Apple did with points is that you don't have to bother with that, though)

try
CGPoint point = [touch locationInView: [touch view]];
CGPoint convertedLocation = [[CCDirector sharedDirector] convertToGL:point];
CGPoint locationInSelf = [self convertToNodeSpace:convertedLocation];
i think the appropriate scale factor is applied by cocos in convertToNodeSpace. Relying on the API will make your code survive any potential future changes (either in cocos or in apple's stuff).
As for concerns on retina, my total work time was near zero (only had to handle cases where i got creative and strayed from the above statement on 'survival', in the non HD version).

CCLOG(#"After x:%f y:%f", point.x * factor, point.y * factor);
here no need to multiply using factor.
Because in retina display 640 * 960 is pixel size and for touch handling they return point so remove factor multiplication . may be its help you.

Related

Problem adapting scale factor for iPad x2 compatibility mode

I wonder if anyone can help me with following. I have written a Quartz 2d ap and have used the following code to get the correct scale factor for each device :
if ([UIScreen instancesRespondToSelector:#selector(scale)])
{
return [[UIScreen mainScreen] scale];
}
else
{
return 1.0;
}
I then multiply all values by this scale mulitplier in my code. The problem I have is that the app does not display propertly in x2 mode on the ipad - everything is two times too big. Can anybody help me ?
Thanks,
Martin
The scale factor is related to the Retina displays on the newer iPhones and iPod touches, not the 2X scaling setting on the iPad. In fact, the UIScreen scale property you are referencing does not exist on the iPad's current 3.2 OS version, only on 4.0+. On the current iPads running the OS 4.2 beta, it should always return 1.0.
The problem you are experiencing with Quartz drawing in the 2X mode must come from somewhere else. Do you do any device-specific checks for any elements in your code?
I am not sure if this is your problem, but you seem to want to test the UIScreen for the selector scale. Which it will never have. That selector works only on [UIScreen mainScreen].
if ([[UIScreen mainScreen] respondsToSelector:#selector(scale)])
{
return [[UIScreen mainScreen] scale];
}
else
{
return 1.0;
}
Although, this mistake would let you think that it would always return a scale of 1.0.

Is there a safe way to find the display size in pixels in universal app?

I get some strange problems when trying to get the screen size in my universal iPhone/iPad app.
I was first using
[[UIScreen mainScreen] bounds]
But it does not return the correct size for iPhone 4 (at least not in the simulator), it just returns 320x480 for all iPhones
Then I changed to
CGSize screenSize = mainscr.currentMode.size;
And it works in the simulator for all apple devices, but when running this line on an iPhone 3GS device the program exits with a SIGABRT
Device is running 3.1.2
Any idea how to get the pixel dimension of the display in a device safe way?
UIScreen.currentMode is not available in < 3.2, so you need to check with -respondsToSelector:
CGSize screenSize;
if ([mainscr respondsToSelector:#selector(currentMode)])
screenSize = mainscr.currentMode.size;
else
screenSize = mainscr.bounds.size;
Similarly, UIScreen.scale is not available in < 4.0, if you use that, check with -respondsToSelector:
.
CGFloat scale = [mainscr respondsToSelector:#selector(scale)] ? mainscr.scale : 1.0f;
[[UIScreen mainScreen] bounds] returns a value in points not in pixels but you can use the scale parameter to convert the resolution in pixels.
The proper way to think about is that the screen resolution IS 320x480 but with a display scale of 2.0. Realize that it is very likely that 'other' apple devices in the future will have other display scales.. imagine for example a new iPad someday that has a scale of 1.5...
if([[UIScreen mainScreen] respondsToSelector:#selector"scale"]) {
displayScale = [[UIScreen mainScreen] scale]; }
The reason they did this is to make it easy to write apps that work on any device. You can put an object on the screen at 100,100 and it will be in the same place on both devices. Use the #2x naming method to provide two sets of images, one at 1x scale and one at 2x scale.

iPhone 4 incorrect screen size?

Could someone tell me why the following code would return Screen Size: 320.000000, 480.000000 on the iPhone 4?
CGRect screenRect = [[UIScreen mainScreen] bounds];
NSLog(#"Screen Size: %f, %f", screenRect.size.width, screenRect.size.height);
That method returns a size in Points, not Pixels. If you are a registered apple developer, I would suggest watching the WWDC (new) video on designing for the Retina display. It has a load of really useful information.
UIKit uses points, however OpenGL uses pixels. UIViews now have a contentScaleFactor property, which will be 2 on iPhone 4, and 1 on everything else so far...

Cocos2d for this?

I just finished my concept for an iphone app. I have a main program and in that program I want to start a game.
MAIN PROGRAM (BUTTON 1 / BUTTON 2 / START GAME)
|
|
Cocos2d Game
Is this possible? To use cocos2D in a normal "iphone app"???
Thanks so much!!!
It is definitely possible to mix UIKit with Cocos2d. Usually people accomplish this by putting their UIKit views inside a Cocos2d layer. It sounds like you want to embed the Cocos2d game inside a UIKit view instead. This should be possible, but I am unsure of the specifics involved. Try looking at -(BOOL)attachInView:(UIView *) view in CCDirector.
Note that Cocos2d uses OpenGL and there are possible performance issues when mixing OpenGL and UIKit. Pausing Cocos2d when using the other part of your application will help.
I found a vast array of great tutorials on Cocos2D for iPhone at Ray Wenderlich's site. One tutorial you might especially want to check out is
How to integrate Cocos2D and UIKit
Note: the original "Cocos2D" framework was written in Python and not designed for iOS; here's the actual homepage for the iOS port.
It is possible, but you should know that the main core of your app must be controlled by Cocos2D, that means CCDirector needs to be instantiated in AppDelegate.
Start with:
a). install Cocos2D:
Go to Terminal and write (if you save cocos2D file in desktop, if not use the path where you save it)
cd Desktop/cocos2d-iphone-1.1-beta
sudo ./install-templates.sh
and if you couldn't install try again with
./install-templates.sh -f -u
b). Import cocos2d.h inside AppDelegate
c). Declare CCDirector in applicationDidFinishLaunching method inside
AppDelegate (you should check the code for this in Cocos2D template is pretty straight
forward)
I did this in my applicationDidFinishLaunching method:
- (void)applicationDidFinishLaunching:(UIApplication *)application {
window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
[window setUserInteractionEnabled:YES];
[window setMultipleTouchEnabled:YES];
CCDirector *director = [CCDirector sharedDirector];
// Create an EAGLView with a RGB8 color buffer, and a depth buffer of 24-bits
EAGLView *glView = [EAGLView viewWithFrame:[window frame]
pixelFormat:kEAGLColorFormatRGB565 // kEAGLColorFormatRGBA8
depthFormat:0 // GL_DEPTH_COMPONENT16_OES
];
[glView setMultipleTouchEnabled:YES];
// attach the openglView to the director
[director setOpenGLView:glView];
// Enables High Res mode (Retina Display) on iPhone 4 and maintains low res on all other devices
if( ! [director enableRetinaDisplay:YES] )
CCLOG(#"Retina Display Not supported");
// make the OpenGLView a child of the main window
[window addSubview:glView];
// make main window visible
[window makeKeyAndVisible];
GameScene *gs = [GameScene node];
[[CCDirector sharedDirector] runWithScene:gs];
//General is part of UIKit where I load the tabbar
General *principal;
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
principal = [[General alloc] initWithNibName:#"General-iPad" bundle:nil];
} else {
principal = [[General alloc] initWithNibName:#"General" bundle:nil];
}
self.general = principal;
[principal release];
[self showUIViewController:general];
}
To push a scene from tabbar in UIKit
[[CCDirector sharedDirector] pushScene: [CCTransitionMoveInB transitionWithDuration:0.0f scene:[MyScene scene]]];
Next I recommend you to follow Ray Wenderlich's tutorial How to integrate Cocos2D and UIKit and read this post aswell how we can show UIViewController and UIView by using cocos2d? Maybe you should search for some code in the Cocos2D forums too, always help to see that.
EDIT:
1) You can download a easy example from here: UIKit Cocos2D
You could check out this framework too: Kobol2D "is an extended and improved version of the popular Cocos2D for iPhone game engine. Everything you know about Cocos2D can still be applied, and Kobold2D is easier to get started with, more convenient to use, more powerful and flexible than Cocos2D with all the documentation available online and offline. Use Kobold2D to develop iPhone, iPod touch, iPad and Mac OS X games for both Apple App Stores. And it has some examples using UIKIT and Cocos2D".
You can certainly create a game with your requirements in Cocos2D. You might want to ask some follow-up questions over at the Cocos2D forums and read through their documentation.
You can probably do this easily......
You have to only make the (UIView *) view in CCDirector.
after attaching the view.....
Yes.It is very easy and handy to implement Cocos2D with normal Iphone FrameWork.Refer Ray Wenderlich UIKit and Cocos2D Integration Tutorial here.It will helpful to you.
Yes! Is very possible, only install the Cocos2d and his templates.
Inclusive, I used Cocos2d to create simple apps with a lot of effects :)

Pinch zoom without UIScrollView based control

Is there an "accepted" way of performing (pinch) zoom on a view that is not based on UIScrollView?
There's a code sample by Erica Sadun that does the math for treating touch events as scale/rotate/translate transforms that you can probably borrow from. Basically, it sounds like you want to apply a scaling affine transform. This code doesn't include the niceties of "bouncing" the view when you reach the edges of the content, so you'll have to do that yourself.
Full disclosure: I haven't done this in almost a year. It's likely that there are frameworks now that include much more straightforward support for this feature.
If you are working in iPhone OS 3.2 (for the iPad) or iOS 4 for the iPhone 4, you can use the UIPinchGestureRecognizer class to detect pinch gestures.
This code helps to zoom UIImageView without using UIScrollView.
-(void)HandlePinch:(UIPinchGestureRecognizer*)recognizer{
if ([recognizer state] == UIGestureRecognizerStateEnded) {
NSLog(#"=======Scale Applied===========");
if ([recognizer scale]<1.0f) {
[recognizer setScale:1.0f];
}
CGAffineTransform transform = CGAffineTransformMakeScale([recognizer scale], [recognizer scale]);
imgView.transform = transform;
}
}