martes, 13 de mayo de 2014

Multi-resolution Support in iOS with Cocos2d-x v3

EDIT: I updated this post to include new iPhone models, and to target Android devices. You can check it here.

Hi all!

Cocos2d-x is a branch of Cocos2d, one of the most used frameworks for developing iOS games, and the one I myself used to develop Chubby Buddy. Cocos2d-x was released to target cross-platform development, including iOS, Android and Windows Phone. It's powerful and widely used, but recently version 3 came out and brought some drastic changes with respect to versions 2.x.x.

Cocos2d-x offers a C++ front-end (also a Lua, a Javascript and HTML5 ones, but we'll let those aside in this post) as opposed to the Objective-C front-end provided by its cousin Cocos2d. 

In addition to the language, one of the first changes that a developer may find is the scheme to support multi-resolution support. In Cocos2d, you simply used a scheme based on suffixing assets according to their resolution. Therefore, if you had a bitmap called Player.png and you wanted to support all resolutions in iOS, you had to create four versions of it and name them like this:

Player.png (iPhone low resolution)
Player-hd.png (iPhone retina)
Player-ipad.png (iPad low resolution)
Player-ipadhd.png (iPad retina)

During application launch, you could specify different suffixes, but the above were the default choices.

This scheme is no longer used by Cocos2d-x. Instead, you must specify different directories where you can find the assets for each resolution. This is done in the applicationDidFinishLaunching() method in AppDelegate.cpp file, which is automatically called by the iOS framework during initialization. The resulting code is shown next:

bool AppDelegate::applicationDidFinishLaunching() {
    // initialize director
    auto director = Director::getInstance();
    auto glview = director->getOpenGLView();
    if(!glview) {
        glview = GLView::create("My Game");
        director->setOpenGLView(glview);
    }
    

    // turn off display FPS
    director->setDisplayStats(false);

    // set FPS. the default value is 1.0/60 if you don't call this
    director->setAnimationInterval(1.0 / 60);

    auto screenSize = glview->getFrameSize();
    
    auto fileUtils = FileUtils::getInstance();
    std::vector<std::string> searchPaths;
    
    if (screenSize.width == 2048 || screenSize.height == 2048)
    {
        glview -> setDesignResolutionSize(1536, 2048, ResolutionPolicy::NO_BORDER);
        searchPaths.push_back("ipadhd");
        searchPaths.push_back("ipadsd");
        searchPaths.push_back("iphone5");
        searchPaths.push_back("iphonehd");
        searchPaths.push_back("iphonesd");
    }
    else if (screenSize.width == 1024 || screenSize.height == 1024)
    {
        glview -> setDesignResolutionSize(768, 1024, ResolutionPolicy::NO_BORDER);
        searchPaths.push_back("ipadsd");
        searchPaths.push_back("iphone5");
        searchPaths.push_back("iphonehd");
        searchPaths.push_back("iphonesd");
        
    }
    else if (screenSize.width == 1136 || screenSize.height == 1136)
    {
        glview -> setDesignResolutionSize(640, 1136, ResolutionPolicy::NO_BORDER);
        searchPaths.push_back("iphone5");
        searchPaths.push_back("iphonehd");
        searchPaths.push_back("iphonesd");
        
    }
    else if (screenSize.width == 960 || screenSize.height == 960)
    {
        glview -> setDesignResolutionSize(640, 960, ResolutionPolicy::NO_BORDER);
        searchPaths.push_back("iphonehd");
        searchPaths.push_back("iphonesd");
    }
    else
    {
        searchPaths.push_back("iphonesd");
        glview -> setDesignResolutionSize(320, 480, ResolutionPolicy::NO_BORDER);
    }
    
    fileUtils->setSearchPaths(searchPaths);
    
    //Load your first scene
    auto scene = GameManager::createScene();
    director->runWithScene(scene);

    return true;
}

If we look into the setSearchPaths method, we see that the search path is suffixed to a base search path that is platform-dependent. In the case of Xcode, the base search path is the Resources folder. Therefore, according to previous code, we would need the following directory structure:

Resources/
*ipadhd/  (assets for iPad retina)
*ipadsd/  (assets for iPad non-retina)
*iphone5/ (assets for iPhone5)
*iphonehd/ (assets for iPhone retina)
*iphonesd/ (assets for iPhone)

Note in the code that for each resolution, we also include directories for lower resolutions. We simply want to ensure that if an asset is not found for the targeted resolution, at least we try to find the next one with the best resolution. Also, note that I want to present the game in portrait mode, but it would be better to use screenSize.width and screenSize.height as parameters of setDesignResolutionSize(), just to ensure that everything will work in landscape mode as well. 

Once you have the directory structure, you can drag all the folders in Xcode. In this step, Xcode will ask how you want to deal with these folders. According to some problems found by other people, I suggest you to uncheck "Copy items into destination group's folder (if needed)" and to check "Create folder references for any added folders". You can read more information on this options here

That's all, tell me if you have any problems and we'll try to solve it.
FM

20 comentarios:

  1. If you are setting a different design resolutions for each device resolution, how do you calculate de positions of the elements in the screen (buttons, labels, etc)? And how do you manage resolutions in Android?

    ResponderEliminar
    Respuestas
    1. In general, you should use always relative positions. You can always retrieve the actual resolution from a simple call. Then, you can play with that. Say, for instance, that you want to place something in the middle of the screen. You'd do something like:

      //This is how to retrieve the actual size (in pixels)
      cocos2d::Size visibleSize = Director::getInstance() -> getVisibleSize();

      //You want sprite1 to be in the center of the screen
      sprite1 -> setPosition(visibleSize.x / 2, visibleSize.y / 2);

      //You want next sprite (sprite2) to be right next to the previous one
      sprite2 -> setPosition(visibleSize.x/2 + sprite1 -> getContentSize().width, visibleSize.y/2);

      This works the same for all resolutions as you're not using absolute coordinates.

      Eliminar
  2. Very nice blog post, it is informative and i subscribed for all its future post. there are some useful links, i think i must share here:
    www.mcafee.com/activate
    mcafee.com/activate

    ResponderEliminar
  3. It is very helpful and informative blog post. i would like to thankful to you providing such a information i have also have website providing very good information
    www.norton.com/setup,
    norton.com/setup

    ResponderEliminar
  4. Great information ! I thankful to author of this blog who sharing such a useful information, I also subscribe your blog for all future post. I have also share some useful links.

    www.mcafee.com/activate
    Mcafee.com/activate
    www.mcafee.com/activate total protection
    www.mcafee.com/activate product key

    ResponderEliminar
  5. Nice information, i was searching of this kind of information, thankyou very much for sharing with us. i also like to share some useful links here

    norton.com/setup,
    www.norton.com/setup.

    ResponderEliminar
  6. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  7. Very nice blog post, it is informative and i subscribed this blog for all its future post. there are some useful links, i think i must share here:
    www.mcafee.com/activate
    mcafee.com/activate
    www.mcafee.com/activate total protection
    www.mcafee.com/activate product key

    ResponderEliminar
  8. Este comentario ha sido eliminado por el autor.

    ResponderEliminar
  9. I am very thankfull to you for sharing this fantastic article , I appereciate your work .
    norton.com/setup
    www.norton.com/setup

    ResponderEliminar
  10. I am very thankfull to you for sharing this fantastic article , I appereciate your work .
    norton.com/setup
    www.norton.com/setup

    ResponderEliminar
  11. Very nice blog post, it is informative and i subscribed for all its future post. there are some useful links, i think i must share here www.mcafee.com/activate
    McAfee.com/activate
    www.mcafee.com/activate total protection

    ResponderEliminar
  12. Great to read such a useful information.i like the way author describe on this topic.thanks i have something to share www.norton.com/setup
    norton.com/setup

    ResponderEliminar
  13. Thank you so much for this Post and all the best for your future. You are really a talented person I have ever seen. I am satisfied with the arrangement of your post i think i must share here www.office.com/setup
    office.com/setup

    ResponderEliminar
  14. Really great article, Glad to read the article. It is very informative for us. Thanks for posting. if you face MS office setup issue or error visit here office.com/setup.

    ResponderEliminar
  15. Go for the best services and devices to help yourself. Talk to the customer care executives with the free spirit if some kinds of issues stop you to make the necessary decisions. visit here avg.com/retail

    ResponderEliminar
  16. Go for the best services and devices to help yourself. Talk to the customer care executives with the free spirit if some kinds of issues stop you to make the necessary decisions. visit heremcafee.com/activate

    ResponderEliminar
  17. I am Hanna Waker , one search point give without toll-free +1(844) 908 0801 to finish help for comprehending all quarries identified with quickbooks Support number. our quickbooks support group are avaiable 24*7.

    ResponderEliminar
  18. Very nice blog post, it is informative and i subscribed for all its future post. there are some useful links, i think i must share here mcafee.com/activate

    ResponderEliminar