Disallow orientation in a certain CCLayer only - cocos2d-iphone

Globally my game supports two orientations: landscape right and landscape left
In one subscreen (inheriting CCLayer) I need to lock the current orientation so that ... the current orientation is locked...
When the user pops back to another screen (CCLayer), orientation should work freely again.

I did it like this:
Edit AppDelegate.h, add a mask for locking orientation:
#interface MyNavigationController : UINavigationController <CCDirectorDelegate>
#property UIInterfaceOrientationMask lockedToOrientation;
#end
In AppDelegate.m, synthesize the mask, and replace two functions:
#synthesize lockedToOrientation; // assign
-(NSUInteger)supportedInterfaceOrientations {
if (!self.lockedToOrientation) {
// iPhone only
if( [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone )
return UIInterfaceOrientationMaskLandscape;
// iPad only
return UIInterfaceOrientationMaskLandscape;
}
else {
return self.lockedToOrientation;
}
}
// Supported orientations. Customize it for your own needs
// Only valid on iOS 4 / 5. NOT VALID for iOS 6.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
if (!self.lockedToOrientation) {
// iPhone only
if( [[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone )
return UIInterfaceOrientationIsLandscape(interfaceOrientation);
// iPad only
// iPhone only
return UIInterfaceOrientationIsLandscape(interfaceOrientation);
}
else {
// I don't need to change this at this point
return NO;
}
}
Then whenever I need to lock interface to a certain orientation, I access navController in the appdelegate. Check its interfaceOrientation property and set locked mask accordingly
AppController* appdelegate = (AppController*)[UIApplication sharedApplication].delegate;
const UIDeviceOrientation ORIENTATION = appdelegate.navController.interfaceOrientation;
appdelegate.navController.lockedToOrientation = ORIENTATION == UIInterfaceOrientationLandscapeLeft ? UIInterfaceOrientationMaskLandscapeLeft : UIInterfaceOrientationMaskLandscapeRight;
In dealloc or whenever I wanna remove the lock, I do this:
AppController* appdelegate = (AppController*)[UIApplication sharedApplication].delegate;
appdelegate.navController.lockedToOrientation = 0;

Related

Cocos2dx v.3.0 Portrait Mode

Hey I am developing a Cocos2dx application with XCode and iPhone 5c and I am looking to change the coordinate system to portrait.
I looked at these directions http://www.cocos2d-x.org/wiki/Device_Orientation. According to the direction, you need to change a couple of methods in the RootViewController so that before Cocos does its magic, iOS has already handled rotating the container.
Now the applicable methods of my RootViewController.mm look as follows. It builds and runs.
#ifdef __IPHONE_6_0
- (NSUInteger) supportedInterfaceOrientations{
return UIInterfaceOrientationMaskPortrait;
}
#endif
- (BOOL) shouldAutorotate {
return YES;
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return UIInterfaceOrientationIsPortrait ( interfaceOrientation );
}
- (void)didRotateFromInterfaceOrientation:(UIInterfaceOrientation)fromInterfaceOrientation {
[super didRotateFromInterfaceOrientation:fromInterfaceOrientation];
auto glview = cocos2d::Director::getInstance()->getOpenGLView();
if (glview)
{
CCEAGLView *eaglview = (__bridge CCEAGLView *)glview->getEAGLView();
if (eaglview)
{
CGSize s = CGSizeMake([eaglview getWidth], [eaglview getHeight]);
cocos2d::Application::getInstance()->applicationScreenSizeChanged((int) s.width, (int) s.height);
}
}
}
#end
The code for the init() method of my level looks like this
bool Level1::init()
{
//////////////////////////////
// 1. super init first
if ( !Scene::init() )
{
return false;
}
auto visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();
for(int i= 0; i < MAX_TOUCHES; ++i) {
labelTouchLocations[i] = Label::createWithSystemFont("", "Arial", 42);
labelTouchLocations[i]->setVisible(false);
this->addChild(labelTouchLocations[i]);
}
auto eventListener = EventListenerTouchAllAtOnce::create();
// Create an eventListener to handle multiple touches, using a lambda, cause baby, it's C++11
eventListener->onTouchesBegan = [=](const std::vector<Touch*>&touches, Event* event){
// Clear all visible touches just in case there are less fingers touching than last time
std::for_each(labelTouchLocations,labelTouchLocations+MAX_TOUCHES,[](Label* touchLabel){
touchLabel->setVisible(false);
});
// For each touch in the touches vector, set a Label to display at it's location and make it visible
for(int i = 0; i < touches.size(); ++i){
labelTouchLocations[i]->setPosition(touches[i]->getLocation());
labelTouchLocations[i]->setVisible(true);
labelTouchLocations[i]->setString("Touched");
std::cout << "(" << touches[i]->getLocation().x << "," << touches[i]->getLocation().y << ")" << std::endl;
}
};
_eventDispatcher->addEventListenerWithSceneGraphPriority(eventListener, this);
return true;
}
When I touch the point at the lowest left part of the iPhone, the x coordinate of the touch point I print out is about 150. The y coordinate is 0, as expected. Why is the x coordinate not zero? Is there something I am missing in the creation of the scene? I believe I made all the changes that the Cocos documentation requires. Are their docs out of date?
The coordinate system does not start at (0,0) at the bottom left as would be suggested by the documentation. However, there are simple methods that can be used to get the origin and size of the container that are included in the default scene.
auto visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();

Change menuItemLabel color when touch starts

Hey I can't find an answer to this anywhere. I want to change my menuItemLabel's font colour when touched. By default it scales and animates to be bigger when you first touch it, but how to customize it more? Here is my label and menuitem:
//add options label
optionsLabelSettings = Label::createWithTTF("OPTIONS", "fonts/font1.ttf", 140);
optionsLabelSettings->setColor(Color3B(255,255,255));
auto optionsItem = MenuItemLabel::create(optionsLabelSettings, CC_CALLBACK_1(MainMenuScene::GoToOptionsScene, this));
optionsItem->setPosition( Point (visibleSize.width/2 + origin.x, visibleSize.height /2));
//add menu
auto menu = Menu::create(optionsItem, NULL);
menu->setPosition( Point(0,0));
this->addChild(menu);
If you want to change font color, you can insert such method in your handler:
void MainMenuScene::GoToOptionsScene(parameters)
{
optionsItem->getLabel()->setColor(Color3B::BLACK); // or any other color
… // your method of switching to another scene
}
If you switch to another scene after pressing, it will be ok.
But if you plan to stay on the current scene, this method is not suitable, because you can’t to restore the font color back. In this case better way is using MenuItemImage and creating images for normal and selected states:
MenuItemImage *optionsItem = MenuItemImage::create(«normalImage.png», «selectedImage.png», CC_CALLBACK_1(MainMenuScene::GoToOptionsScene, this));
Or using ui::Button, if you haven’t images:
ui::Button* optionsItem = ui::Button::create();
optionsItem->setPosition(…);
optionsItem->setTitleText(«OPTIONS»);
optionsItem->setTitleFontName("fonts/font1.ttf");
optionsItem->setTitleFontSize(140);
optionsItem->setTitleColor(Color3B::WHITE);
optionsItem->addTouchEventListener([&](Ref* sender, Widget::TouchEventType type){
switch (type)
{
case ui::Widget::TouchEventType::BEGAN:
this->startPressingGoToOptionsScene();
break;
case ui::Widget::TouchEventType::ENDED:
this->finishPressingGoToOptionsScene();
break;
default:
break;
}
});
this->addChild(optionsItem);
and then in each handler set different button behavior:
void MainMenuScene::startPressingGoToOptionsScene(parameters)
{
optionsItem->setTitleColor(Color3B::BLACK);
optionsItem->setTitleText(«…») // change anything else
…
}
void MainMenuScene::finishPressingGoToOptionsScene(parameters)
{
optionsItem->setTitleColor(Color3B::WHITE); // return original font color and other changes
…
}
customise your MenuItemLabel class & Implement following two methods :-
class ChangedMenuItemLabel : MenuItemLabel
{
bool ChangedMenuItemLabel::initWithLabel(cocos2d::Node *label, const ccMenuCallback &callback)
{
if (!MenuItemLabel::initWithLabel(label,callback)) {
return false;
}
return true;
}
void selected(){
this->setColor("your color");
}
void unselected(){
this->setColor("your color");//or nothing
}
}

Cocos2D HD spriteWithFile displays in SD

I'm using Cocos2D 2.1 and this app is planned to be iPad only retina and non-retina. When I use spriteWithFile, and specify the filename without the -hd suffix, Cocos2D finds the image with the -hd suffix and uses it, but it displays in standard definition, meaning it doesn't scale down so it's displaying as a retina image.
In my delegate's - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions method, I have the following:
CCGLView *glView = [CCGLView viewWithFrame:[window_ bounds]
pixelFormat:kEAGLColorFormatRGBA8
depthFormat:GL_DEPTH24_STENCIL8_OES
preserveBackbuffer:NO
sharegroup:nil
multiSampling:NO
numberOfSamples:0];
[glView setMultipleTouchEnabled:YES];
director_ = (CCDirectorIOS*) [CCDirector sharedDirector];
[director_ setDisplayStats:YES];
[director_ setAnimationInterval:1.0/60];
// attach the openglView to the director
[director_ setView:glView];
// 2D projection
[director_ setProjection:kCCDirectorProjection2D];
[director_ enableRetinaDisplay:YES];
// // Enables High Res mode (Retina Display) on iPhone 4 and maintains low res on all other devices
if( ! [director_ enableRetinaDisplay:NO] )
CCLOG(#"Retina Display Not supported");
// If the 1st suffix is not found and if fallback is enabled then fallback suffixes are going to searched. If none is found, it will try with the name without suffix.
CCFileUtils *sharedFileUtils = [CCFileUtils sharedFileUtils];
[sharedFileUtils setEnableFallbackSuffixes:NO]; // Default: NO. No fallback suffixes are going to be used
[sharedFileUtils setiPadRetinaDisplaySuffix:#"-hd"];
glView.opaque = NO;
glClearColor(0.0f,0.0f,0.0f,0.0f);
//glClear(GL_COLOR_BUFFER_BIT);
// Assume that PVR images have premultiplied alpha
[CCTexture2D PVRImagesHavePremultipliedAlpha:YES];
When I add the sprite using spriteWithFile (instead of using a sprite sheet because it's only one image) I have the following code:
movingTileGroup.handleSprite1 = [CCSprite spriteWithFile:#"draggableTileGroupHandle.png"];
movingTileGroup.handleSprite1.position = ccp(x,y);
[self.theMap addChild:movingTileGroup.handleSprite1 z:13];
Update: I see when -(BOOL) enableRetinaDisplay:(BOOL)enabled is called, none of the if statements return true. Could it have to do with a scale factor setting?
-(BOOL) enableRetinaDisplay:(BOOL)enabled
{
NSLog(#"enableRetinaDisplay called");
// Already enabled ?
if( enabled && __ccContentScaleFactor == 2 ){
NSLog(#"enabled && __ccContentScaleFactor == 2 ");
return YES;
}
// Already disabled
if( ! enabled && __ccContentScaleFactor == 1 ){
NSLog(#"! enabled && __ccContentScaleFactor == 1 ");
return YES;
}
// setContentScaleFactor is not supported
if (! [__view respondsToSelector:#selector(setContentScaleFactor:)]){
NSLog(#"setContentScaleFactor is not supported");
return NO;
}
// SD device
if ([[UIScreen mainScreen] scale] == 1.0){
NSLog(#"SD device");
return NO;
}
float newScale = enabled ? 2 : 1;
[self setContentScaleFactor:newScale];
// Load Hi-Res FPS label
[[CCFileUtils sharedFileUtils] buildSearchResolutionsOrder];
[self createStatsLabel];
return YES;
}
I am assuming you are using Spritebuilder to generate sprites , if that is so you need to specify the default size of the image in the spritebuilder software, this can be done by clicking on the image which is showing the problem and then on the left sidebar or panel select the image size as "default" and it will automatically scale to the required size...
If this doesn't solves your problem, Do let me know... regards...

Limit UIPanGestureRecognizer to a specific area

In my view I have in the right part 3 UISlider and in the left part the user can slide the menu with a UIPanGestureRecognizer. Sometimes, when the user uses the slider, it also drag the view. I don't want this to happen. I have this code in my panLayer method:
- (IBAction)panLayer:(UIPanGestureRecognizer *)pan {
if (pan.state == UIGestureRecognizerStateChanged) {
CGPoint point= [pan translationInView:self.topLayer];
CGRect frame = self.topLayer.frame;
frame.origin.x = MIN(258.0f, self.layerPosition + point.x);
if (frame.origin.x < 0) frame.origin.x = 0;
self.topLayer.frame = frame;
}
if (pan.state == UIGestureRecognizerStateEnded) {
if (self.topLayer.frame.origin.x <=160) {
[self animateLayerToPoint:0];
}
else {
[self animateLayerToPoint:VIEW_HIDDEN];
}
}
}
I want to create an if statement with the x position. Something like this:
if (firstTouch.x < 150 && firstTouch.x > 0) {
//Add my previous code
}
How can I access to the firstTouch in the view, and if it is in this area, run the UIPanGestureRecognizer?
Thanks
This can be easily achieved by adopting UIGestureRecognizerDelegate and implementing the following method:
-(BOOL)gestureRecognizer:(UIGestureRecognizer*)gestureRecognizer shouldReceiveTouch:(UITouch*)touch
{
return touch.view != myUISlider;
}

cocos2d + GKMatchmakerViewController causes portrait mode

I'm implementing Game Kit in a cocos2d game project.
The game is landscape orientation only. And so should gamekit also be.
When I present the gamekit modal viewcontroller for matchmaking, it displays in landscape. But the underlying cocos2d CCLayer becomes portrait.
The rootViewContollers orientation code looks like this:
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
//
// There are 2 ways to support auto-rotation:
// - The OpenGL / cocos2d way
// - Faster, but doesn't rotate the UIKit objects
// - The ViewController way
// - A bit slower, but the UiKit objects are placed in the right place
//
#if GAME_AUTOROTATION==kGameAutorotationNone
//
// EAGLView won't be autorotated.
// Since this method should return YES in at least 1 orientation,
// we return YES only in the Portrait orientation
//
return ( interfaceOrientation == UIInterfaceOrientationPortrait );
#elif GAME_AUTOROTATION==kGameAutorotationCCDirector
//
// EAGLView will be rotated by cocos2d
//
// Sample: Autorotate only in landscape mode
//
if( interfaceOrientation == UIInterfaceOrientationLandscapeLeft ) {
[[CCDirector sharedDirector] setDeviceOrientation: kCCDeviceOrientationLandscapeRight];
} else if( interfaceOrientation == UIInterfaceOrientationLandscapeRight) {
[[CCDirector sharedDirector] setDeviceOrientation: kCCDeviceOrientationLandscapeLeft];
}
// Since this method should return YES in at least 1 orientation,
// we return YES only in the Portrait orientation
return ( UIInterfaceOrientationIsLandscape(interfaceOrientation) );
#elif GAME_AUTOROTATION == kGameAutorotationUIViewController
//
// EAGLView will be rotated by the UIViewController
//
// Sample: Autorotate only in landscpe mode
//
// return YES for the supported orientations
return ( UIInterfaceOrientationIsLandscape( interfaceOrientation ) );
#else
#error Unknown value in GAME_AUTOROTATION
#endif // GAME_AUTOROTATION
// Shold not happen
return NO;
}
It makes no difference if I define GAME_AUTOROTATION to kGameAutorotationUIViewController or kGameAutorotationCCDirector or kGameAutorotationNone
So I believe I have found a fix.
In my AppDelegate I made these changes:
//[window addSubview: viewController.view];
[window setRootViewController:viewController];
Then orientation worked perfectly on iOS6, but on iOS versions prior to iOS6 the width and height of the window size was reversed, and caused issues for the game. I solved that by adding a blank scene before running my normal scene, because the reverse size was fixed by itself when pushing a new scene. From the blank scene I ran [[CCDirector sharedDirector] replaceWithScene:[Game scene]]; after a delay, and it works now.