Multi-resolution code in the default template

Come on, you perfectly explained your idea of how to calculate scaleFactor to avoid black borders, now you only need to use setContentScaleFactor() and then the engine will be apply scaleFactor to all sprites automatically.

I do not want the engine to take care of the images. Because, in case of engine, it will just take the MIN or MAX value of the ratios. But then there will be no guarantee of black border’s absence. That’s why I am using contentScaleFactor = 1 and implementing my scaleFactor to guarantee black border’s absence.

If you will use setContentScaleFactor(yourScaleFactor), then you will not get black borders. 100%

It is based on your code.

#include "AppDelegate.h"
#include "HelloWorldScene.h"

// #define USE_AUDIO_ENGINE 1
// #define USE_SIMPLE_AUDIO_ENGINE 1

#if USE_AUDIO_ENGINE && USE_SIMPLE_AUDIO_ENGINE
#error "Don't use AudioEngine and SimpleAudioEngine at the same time. Please just select one in your game!"
#endif

#if USE_AUDIO_ENGINE
#include "audio/include/AudioEngine.h"
using namespace cocos2d::experimental;
#elif USE_SIMPLE_AUDIO_ENGINE
#include "audio/include/SimpleAudioEngine.h"
using namespace CocosDenshion;
#endif

USING_NS_CC;

static cocos2d::Size designResolutionSize = cocos2d::Size(1280, 720);


AppDelegate::AppDelegate()
{
}

AppDelegate::~AppDelegate() 
{
#if USE_AUDIO_ENGINE
    AudioEngine::end();
#elif USE_SIMPLE_AUDIO_ENGINE
    SimpleAudioEngine::end();
#endif
}

// if you want a different context, modify the value of glContextAttrs
// it will affect all platforms
void AppDelegate::initGLContextAttrs()
{
    // set OpenGL context attributes: red,green,blue,alpha,depth,stencil
    GLContextAttrs glContextAttrs = {8, 8, 8, 8, 24, 8};

    GLView::setGLContextAttrs(glContextAttrs);
}

// if you want to use the package manager to install more packages,  
// don't modify or remove this function
static int register_all_packages()
{
    return 0; //flag for packages manager
}

bool AppDelegate::applicationDidFinishLaunching() {
    // initialize director
    auto director = Director::getInstance();
    auto glview = director->getOpenGLView();
    if(!glview) {
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_MAC) || (CC_TARGET_PLATFORM == CC_PLATFORM_LINUX)
        glview = GLViewImpl::createWithRect("HelloCpp", cocos2d::Rect(0, 0, designResolutionSize.width, designResolutionSize.height));
#else
        glview = GLViewImpl::create("HelloCpp");
#endif
        director->setOpenGLView(glview);
    }

    // turn on display FPS
    director->setDisplayStats(true);

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

    // Set the design resolution
    //glview->setDesignResolutionSize(designResolutionSize.width, designResolutionSize.height, ResolutionPolicy::NO_BORDER);
    auto frameSize = glview->getFrameSize();

	if ((frameSize.width / frameSize.height) > (designResolutionSize.width / designResolutionSize.height))
	{
		director->setContentScaleFactor(frameSize.width / designResolutionSize.width);
	}
	else // (frameSize.width / frameSize.height) < (designResolutionSize.width / designResolutionSize.height)
	{
		director->setContentScaleFactor(frameSize.height / designResolutionSize.height);
	}

    register_all_packages();

    // create a scene. it's an autorelease object
    auto scene = HelloWorld::createScene();

    // run
    director->runWithScene(scene);

    return true;
}

// This function will be called when the app is inactive. Note, when receiving a phone call it is invoked.
void AppDelegate::applicationDidEnterBackground() {
    Director::getInstance()->stopAnimation();

#if USE_AUDIO_ENGINE
    AudioEngine::pauseAll();
#elif USE_SIMPLE_AUDIO_ENGINE
    SimpleAudioEngine::getInstance()->pauseBackgroundMusic();
    SimpleAudioEngine::getInstance()->pauseAllEffects();
#endif
}

// this function will be called when the app is active again
void AppDelegate::applicationWillEnterForeground() {
    Director::getInstance()->startAnimation();

#if USE_AUDIO_ENGINE
    AudioEngine::resumeAll();
#elif USE_SIMPLE_AUDIO_ENGINE
    SimpleAudioEngine::getInstance()->resumeBackgroundMusic();
    SimpleAudioEngine::getInstance()->resumeAllEffects();
#endif
}

In this case, you do not need to scale each sprite manually.
And you can always get scaleFactor by director->getContentScaleFactor().

I tried this but did not get what I wanted. That’s why I moved to my solution.

My bad. It should be setContentScaleFactor(1 / yourScaleFactor).

Here is the correct version.

AppDelegate.cpp

#include "AppDelegate.h"
#include "HelloWorldScene.h"

// #define USE_AUDIO_ENGINE 1
// #define USE_SIMPLE_AUDIO_ENGINE 1

#if USE_AUDIO_ENGINE && USE_SIMPLE_AUDIO_ENGINE
#error "Don't use AudioEngine and SimpleAudioEngine at the same time. Please just select one in your game!"
#endif

#if USE_AUDIO_ENGINE
#include "audio/include/AudioEngine.h"
using namespace cocos2d::experimental;
#elif USE_SIMPLE_AUDIO_ENGINE
#include "audio/include/SimpleAudioEngine.h"
using namespace CocosDenshion;
#endif

USING_NS_CC;

static cocos2d::Size designResolutionSize = cocos2d::Size(1280, 720);


AppDelegate::AppDelegate()
{
}

AppDelegate::~AppDelegate() 
{
#if USE_AUDIO_ENGINE
    AudioEngine::end();
#elif USE_SIMPLE_AUDIO_ENGINE
    SimpleAudioEngine::end();
#endif
}

// if you want a different context, modify the value of glContextAttrs
// it will affect all platforms
void AppDelegate::initGLContextAttrs()
{
    // set OpenGL context attributes: red,green,blue,alpha,depth,stencil
    GLContextAttrs glContextAttrs = {8, 8, 8, 8, 24, 8};

    GLView::setGLContextAttrs(glContextAttrs);
}

// if you want to use the package manager to install more packages,  
// don't modify or remove this function
static int register_all_packages()
{
    return 0; //flag for packages manager
}

bool AppDelegate::applicationDidFinishLaunching() {
    // initialize director
    auto director = Director::getInstance();
    auto glview = director->getOpenGLView();
    if(!glview) {
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_MAC) || (CC_TARGET_PLATFORM == CC_PLATFORM_LINUX)
        glview = GLViewImpl::createWithRect("Multi-resolution", cocos2d::Rect(0, 0, 640, 480));
#else
        glview = GLViewImpl::create("Multi-resolution");
#endif
        director->setOpenGLView(glview);
    }

    // turn on display FPS
    director->setDisplayStats(true);

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

    // Set the design resolution
    //glview->setDesignResolutionSize(designResolutionSize.width, designResolutionSize.height, ResolutionPolicy::NO_BORDER);
    auto frameSize = glview->getFrameSize();

	if ((frameSize.width / frameSize.height) > (designResolutionSize.width / designResolutionSize.height))
	{
		//director->setContentScaleFactor(frameSize.width / designResolutionSize.width);
		auto scale = frameSize.width / designResolutionSize.width;
		director->setContentScaleFactor(1 / scale);
	}
	else // (frameSize.width / frameSize.height) < (designResolutionSize.width / designResolutionSize.height)
	{
		//director->setContentScaleFactor(frameSize.height / designResolutionSize.height);
		auto scale = frameSize.height / designResolutionSize.height;
		director->setContentScaleFactor(1 / scale);
	}

    register_all_packages();

    // create a scene. it's an autorelease object
    auto scene = HelloWorld::createScene();

    // run
    director->runWithScene(scene);

    return true;
}

// This function will be called when the app is inactive. Note, when receiving a phone call it is invoked.
void AppDelegate::applicationDidEnterBackground() {
    Director::getInstance()->stopAnimation();

#if USE_AUDIO_ENGINE
    AudioEngine::pauseAll();
#elif USE_SIMPLE_AUDIO_ENGINE
    SimpleAudioEngine::getInstance()->pauseBackgroundMusic();
    SimpleAudioEngine::getInstance()->pauseAllEffects();
#endif
}

// this function will be called when the app is active again
void AppDelegate::applicationWillEnterForeground() {
    Director::getInstance()->startAnimation();

#if USE_AUDIO_ENGINE
    AudioEngine::resumeAll();
#elif USE_SIMPLE_AUDIO_ENGINE
    SimpleAudioEngine::getInstance()->resumeBackgroundMusic();
    SimpleAudioEngine::getInstance()->resumeAllEffects();
#endif
}

HelloWorldScene.cpp

#include "HelloWorldScene.h"
#include "SimpleAudioEngine.h"

USING_NS_CC;

Scene* HelloWorld::createScene()
{
    return HelloWorld::create();
}

// Print useful error message instead of segfaulting when files are not there.
static void problemLoading(const char* filename)
{
    printf("Error while loading: %s\n", filename);
    printf("Depending on how you compiled you might have to add 'Resources/' in front of filenames in HelloWorldScene.cpp\n");
}

// on "init" you need to initialize your instance
bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !Scene::init() )
    {
        return false;
    }

	auto frameSize = Director::getInstance()->getOpenGLView()->getFrameSize();

    auto visibleSize = Director::getInstance()->getVisibleSize();
    auto origin = Director::getInstance()->getVisibleOrigin();

	// back
	auto back = Sprite::create("1280x720.png");
	back->setPosition(frameSize.width / 2, frameSize.height / 2);

	this->addChild(back);

	// triangle1
	auto triangle1 = Sprite::create("triangle1.png");
	triangle1->setPosition(triangle1->getContentSize().width / 2, frameSize.height / 2);

	this->addChild(triangle1);

	// triangle2
	auto triangle2 = Sprite::create("triangle2.png");
	triangle2->setPosition(frameSize.width / 2, frameSize.height - (triangle2->getContentSize().height / 2));

	this->addChild(triangle2);
	
	return true;
}


void HelloWorld::menuCloseCallback(Ref* pSender)
{
    //Close the cocos2d-x game scene and quit the application
    Director::getInstance()->end();

    #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
    exit(0);
#endif

    /*To navigate back to native iOS screen(if present) without quitting the application  ,do not use Director::getInstance()->end() and exit(0) as given above,instead trigger a custom event created in RootViewController.mm as below*/

    //EventCustom customEndEvent("game_scene_close_event");
    //_eventDispatcher->dispatchEvent(&customEndEvent);


}

Resources

1280x720.png

triangle1.png

triangle1

triangle2.png

triangle2

Screen 640x480

Screen 960x540

Screen 854x480

Screen 2160x1080 (x0.5)
(It is LG Q6 screen)

  • Note

Despite the fact that this is working solution, I prefer to use design resolution.

Because if I want to get something like this (the positions of the triangles)

and if I use the design resolution (glview->setDesignResolutionSize(1280, 720, ResolutionPolicy::NO_BORDER)),
then I can use this simple code to get the same result on all devices

auto visibleSize = Director::getInstance()->getVisibleSize();
auto origin = Director::getInstance()->getVisibleOrigin();

// back
auto back = Sprite::create("1280x720.png");
back->setPosition(origin.x + visibleSize.width / 2, origin.y + visibleSize.height / 2);

this->addChild(back);

// triangle1
auto triangle1 = Sprite::create("triangle1.png");
triangle1->setPosition(origin.x + 100 + 70, origin.y + visibleSize.height / 2);

this->addChild(triangle1);

// triangle2
auto triangle2 = Sprite::create("triangle2.png");
triangle2->setPosition(origin.x + 100 + 70 + 200 + 70, origin.y + visibleSize.height / 2);

this->addChild(triangle2);

(The size of the triangles is 200x200, and the distance is 70).

I do not need to scale the distance or the size of the triangles, it will happen automatically.

  • Note 2

And even this is not the final solution.

In most cases, it is a good idea to use multiple sets of graphic resources, so

1 Like

I worked on it for a while to suggest the most universal and intuitive solution. But I accidentally deleted my codes and notes, and I can not restore it. I’m so upset now.

The good thing is that this post will be shorter than it might be, because I write only what I remember.

  • Option 1

Firstly, I do not think this is the best solution ever, but I think that this is a good option to illustrate how this works.

The aspect ratio of the Design Resolution is 16/9 to illustrate NO_BORDER.
Convenient scale factors (x1, x2, x3).

...

static cocos2d::Size designResolutionSize = cocos2d::Size(960, 540);

static cocos2d::Size smallResolutionSize = cocos2d::Size(960, 540);
static cocos2d::Size mediumResolutionSize = cocos2d::Size(1920, 1080);
static cocos2d::Size largeResolutionSize = cocos2d::Size(2880, 1620);

...

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

auto frameSize = glview->getFrameSize();
std::vector<std::string> searchPaths;

if (frameSize.width > mediumResolutionSize.width)
{
   director->setContentScaleFactor(largeResolutionSize.width/designResolutionSize.width);
   searchPaths.push_back("largeResources");
}
else if (frameSize.width > smallResolutionSize.width)
{
   director->setContentScaleFactor(mediumResolutionSize.width/designResolutionSize.width);
   searchPaths.push_back("mediumResources");
}
else
{
   director->setContentScaleFactor(smallResolutionSize.width/designResolutionSize.width);
   searchPaths.push_back("smallResources");
}

FileUtils::getInstance()->setSearchPaths(searchPaths);

...

In this case, I just do not like how it works if the screen aspect ratio < 16/9. Maybe I’m just a perfectionist. But this is a good option to illustrate NO_BORDER. I doubt what to choose.

  • Option 2

The aspect ratio of the Design Resolution is 4/3, not so good to illustrate NO_BORDER, my inner perfectionist absolutely likes how it works.
Convenient scale factors (x1, x2, x3).

...

static cocos2d::Size designResolutionSize = cocos2d::Size(960, 720);

static cocos2d::Size smallResolutionSize = cocos2d::Size(960, 720);
static cocos2d::Size mediumResolutionSize = cocos2d::Size(1920, 1440);
static cocos2d::Size largeResolutionSize = cocos2d::Size(2880, 2160);

...

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

auto frameSize = glview->getFrameSize();
std::vector<std::string> searchPaths;

if (frameSize.width > mediumResolutionSize.width)
{
   director->setContentScaleFactor(largeResolutionSize.width/designResolutionSize.width);
   searchPaths.push_back("largeResources");
}
else if (frameSize.width > smallResolutionSize.width)
{
   director->setContentScaleFactor(mediumResolutionSize.width/designResolutionSize.width);
   searchPaths.push_back("mediumResources");
}
else
{
   director->setContentScaleFactor(smallResolutionSize.width/designResolutionSize.width);
   searchPaths.push_back("smallResources");
}

FileUtils::getInstance()->setSearchPaths(searchPaths);

...
  • Option 3

The aspect ratio of the Design Resolution is 4/3, not so good to illustrate NO_BORDER, more iOS friendly.
Personally, I do not like scale factors.

...

static cocos2d::Size designResolutionSize = cocos2d::Size(1024, 768);

static cocos2d::Size smallResolutionSize = cocos2d::Size(1024, 768);
static cocos2d::Size mediumResolutionSize = cocos2d::Size(2048, 1536);
static cocos2d::Size largeResolutionSize = cocos2d::Size(2880, 2160);

...

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

auto frameSize = glview->getFrameSize();
std::vector<std::string> searchPaths;

if (frameSize.width > mediumResolutionSize.width)
{
   director->setContentScaleFactor(largeResolutionSize.width/designResolutionSize.width);
   searchPaths.push_back("largeResources");
}
else if (frameSize.width > smallResolutionSize.width)
{
   director->setContentScaleFactor(mediumResolutionSize.width/designResolutionSize.width);
   searchPaths.push_back("mediumResources");
}
else
{
   director->setContentScaleFactor(smallResolutionSize.width/designResolutionSize.width);
   searchPaths.push_back("smallResources");
}

FileUtils::getInstance()->setSearchPaths(searchPaths);

...
  • Note

Since there are many similar resolutions like 1920x1080, 1920x1200, 1920x1280, 1920x1440, I prefer to choose the resources based on the longer side of the screen, in the case of the default template it is width.

@slackmoehrle I want to know your opinion. :slight_smile:

1 Like

I mean it works correctly, but in some cases the resources will be scaled up.
eg if screen size is 1920x1440.

In fact, any criticism is welcome.

I will take a look.

Can you please also take a look at SBX? :slight_smile:
It supports any resolution and developing multi resolution games is so simple with it, also it WYSYWYG, so you can check any resolution support in no time by quickly switching between them.

Can you please explain, what SBX will show in the orange area of the iPhone X for your game? And please create a landscape version of it, because most of the AAA game will be in this mode.

It’s iPhone X adaptation as per this requirement Designing for iPhone X in that areas you should have not UI, but game scene.

It’s not matter what portrait or landscape. Resolution system in SBX and it’s loader works the same for any resolution. You can design and see what you will get. If you interested you need to try it. I will not create landscape version. I’m working on SBX and my game every days, so time is money :slight_smile:

Cocos Creator has a similar functional.

So use it.

I just don’t understand why you want to @slackmoehrle look at it when the team has their own product with similar functional.

I remind you.

Sorry. I for some reason thought there was a GitHub conversation going on about this and I didn’t need to be in the loop any longer. There have been so many resolution questions and iPhone X safe area questions that I thought it was already in the right hands.

I created an issue on github after I despaired of receiving a response from you. :slight_smile:

Thank you. I appreciate you creating it.

I think that it will be ignored the same way. So I’ll just close the issue after a while, the github already has more than enough open requests.
Why did I decide that this is interesting to someone… Nevermind. Just forget it.