Monday, November 3, 2014

Multi Resolution Support in Cocos2dx - Windows

For this entry i will explain the best way i found to work with multiple resolutions in Cocos2dx.

The page on the documentation gives you a code snipet:


 typedef struct tagResource
    {
        cocos2d::CCSize size;
        char directory[100];
    }Resource;

    static Resource smallResource  =  { cocos2d::CCSizeMake(480, 320),   "iphone" };
    static Resource mediumResource =  { cocos2d::CCSizeMake(1024, 768),  "ipad"   };
    static Resource largeResource  =  { cocos2d::CCSizeMake(2048, 1536), "ipadhd" };
    static cocos2d::CCSize designResolutionSize = cocos2d::CCSizeMake(480, 320);

    bool AppDelegate::applicationDidFinishLaunching() {
        // initialize director
        CCDirector* pDirector = CCDirector::sharedDirector();
        CCEGLView* pEGLView = CCEGLView::sharedOpenGLView();

        pDirector->setOpenGLView(pEGLView);

        // Set the design resolution
        pEGLView->setDesignResolutionSize(designResolutionSize.width, designResolutionSize.height, ResolutionPolicy::SHOW_ALL);

        CCSize frameSize = pEGLView->getFrameSize();

        std::vector<std::string> searchPath;

        // In this demo, we select resource according to the frame's height.
        // If the resource size is different from design resolution size, you need to set contentScaleFactor.
        // We use the ratio of resource's height to the height of design resolution,
        // this can make sure that the resource's height could fit for the height of design resolution.

        // if the frame's height is larger than the height of medium resource size, select large resource.
        if (frameSize.height > mediumResource.size.height)
        { 
            searchPath.push_back(largeResource.directory);
            pDirector->setContentScaleFactor(largeResource.size.height/designResolutionSize.height);
        }
        // if the frame's height is larger than the height of small resource size, select medium resource.
        else if (frameSize.height > smallResource.size.height)
        { 
            searchPath.push_back(mediumResource.directory);
            pDirector->setContentScaleFactor(mediumResource.size.height/designResolutionSize.height);
        }
        // if the frame's height is smaller than the height of medium resource size, select small resource.
        else
        { 
            searchPath.push_back(smallResource.directory);
            pDirector->setContentScaleFactor(smallResource.size.height/designResolutionSize.height);
        }
        ...................
        ...................
        auto fileUtils = FileUtils::getInstance();
 fileUtils->setSearchPaths(searchPath);
    }
You probably can tell already what the code is trying to do:

  1. Define a serie of resolutions for which you will design resources (low res, hd, etc).
  2. Set a DesignResolution
  3. Select a set of resources based on the frame size of the current device
  4. Send your director a content scale factor so it can know how to resize everything.
  5. Set your directory search path according to your resource selection.
The design resolution is the resolution for which you are designing your game natively and it is very important because everything will be based on it.

But there is another issue other than resolutions: the aspect ratio. There are a ton of device screen sizes and aspect ratios(4:3, 3:2, 16:10, etc). To solve this you have several options (i will be brief on this because on the documentation explain it in detail):
  • Exact fit: App will fill the screen, forgetting about the aspect ratio and stretching or compressing as needed (There can be a noticeable distorsion on your shapes)
  • No border: Your app will fill the screen but it will keep it's aspect ratio. It can crop at 2 of the 4 borders if the aspect ratio of the device is different than the aspect ratio of the design resolution.
  • Show all: This is my favorite. The app will be completely visible in the screen and will keep the same aspect ratio as design resolution. This can cause that 2 of the 4 borders have black borders. There is an easy workaround for this that i will explain in short.
  • Fixed height/Fixed width: As the name implies, it will keep either the height or the width constant and scale the other.
I found a very good explanation in here on using show all policy and fix your black borders (it is for another engine and uses another terms but you should read it, it has very good information).
If you know you can get black borders when you resize, design your backgrounds to be bigger than your current screen.
The recommendation is to work with an aspect ratio of 3:2. If you scale to 4:3 you will have your borders at the top/bottom and if you scale to 16:9 they will be left/right . Those will be your worst case scenarios and you will design your background image to fit it.
Knowing that all you have to do is calculate how much bigger your background will have to be in each direction. The calculations are simple, just do the following:

  • For X axis you have that your worst case is 16:9, that being an aspect ratio can be reduced to a fraction: 16/9 = 1.777777. Having that 3:2 = 3/2 = 1.5 we can conclude that 16:9 is 1.185185 times bigger than 3:2 ( 1.777777/1.5 ). What this implies is that for your supported resolution (ie. 480x320) your X axis will have to be 1.185185 times larger. This is something as simple as 480*1.185185=568.88 (round it to 570px). You will need a background with an X axis of 570px in the worst case, so create your regular 480px background and add it some pretty borders or something to cover the missing 90px (45px in each side). 
  • For the Y axis the procedure is exactly the same but with an aspect ratio of 4:3. In this case 4:3 aspect ratio is 1.125 times bigger than 3:2 (apply the same reasoning as X axis) so you will need to make your Y axis at least 320*1.125=360px
Try to round your calculations to the next higher pair number, so you can add the same number of pixels at both sides and avoid errors on your centering.

Repeat this for every resolution you are going to support (For obvious reasons it is better to start at a high resolution and then scale down because the distortion is not noticeable, but i am doing it this way for the sake of clarity).

If we used 16:9 instead of 3:2 as our design aspect ratio, we would have a worst case of 1.333 times when converting to 4:3, which is worse than using an intermediate aspect ratio as 3:2, so we would have more wasted space to fill.

If you have doubts about this you can check the linked explanation (again, very good and with pictures!!) or leave a comment.

We covered multisupport resolution in cocos2dx and the way to overcome the drawback of a good resolution policy for scaling our app into different aspect ratios.

All that is left is to explain how to test this in windows.

In windows, cocos2dx uses main.cpp to set it's entry point. You can create a frame size there with the desired resolution and the app will behave as it is running in a device with that resolution. You can even scale it to show bigger on your screen to avoid missing details because it is too little!!!

In your main.cpp (project/win32):

#include "main.h"
#include "AppDelegate.h"
#include "cocos2d.h"

USING_NS_CC;

int APIENTRY _tWinMain(HINSTANCE hInstance,
                       HINSTANCE hPrevInstance,
                       LPTSTR    lpCmdLine,
                       int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // create the application instance
    AppDelegate app;
    auto director = Director::getInstance();
    director->setDisplayStats(true);
    auto glview = GLViewImpl::createWithRect("MyApp", Rect(0, 0, 480, 320), 2.0f);
    director->setOpenGLView(glview);

    return Application::getInstance()->run();
}



We create a GLView and set it our resolution and our zoom (the zoom won't change the resolution, just scale it in your window). Then just leave your appdelegate as we saw at the beggining and it should work. You can start making an app that will scale with no major drawbacks in any resolution and aspect ratio!!!

No comments:

Post a Comment