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

19 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. Are you using the same versions of SFML and Qt? Can you post the error messages that you get?
      دانلود آهنگ های جدید

      Eliminar
  2. Este comentario ha sido eliminado por el autor.

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

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

    ResponderEliminar
  5. 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
  6. 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
  7. 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
  8. 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
  9. I am very much pleased with the contents you have mentioned. I wanted to thank you for this great article. Tony Soprano Leather Jacket

    ResponderEliminar
  10. Nice post. Thank you to provide us this useful information.Usmle Step 3

    ResponderEliminar
  11. This Article is one of the best Artice I have ever read. I have Got So many useful information through your blog. Thank you so much and I also want to share some useful links here. mcafee.com/activate, www.mcafee.com/activate, mcafee activation code

    ResponderEliminar
  12. I really enjoyed reading your articles. It looks like you’ve spent a lot of time and effort on your blog. Commissar Coat

    ResponderEliminar
  13. Learn how Healing Buddha's services go beyond physical healing, addressing emotional, mental, and spiritual well-being. Discover the power of holistic balance.
    pranic healing

    ResponderEliminar
  14. The Global Hues explores the interconnectedness of health, wealth, and more, providing insights that contribute to holistic well-being.
    theglobalhues

    ResponderEliminar