How to calculation the posistion of the sprite in multi resolution support game

Hi every one, i have three background image with different size for multi resulution.
The first one background.png with width 640, height 1136, it put this image in Resources\iphone folder
The second one background.png with width 768, height 1024, it put this image in Resources\ipad folder
The last one background.png with width 1536, height 2048, it put this image in Resources\ipadhd folder

In the AppDelegate.cpp i write the code as below.

#include "AppDelegate.h"
#include "HelloWorldScene.h"
#include "SimpleAudioEngine.h"
using namespace CocosDenshion;
USING_NS_CC;

// for multi resolution support
typedef struct tagResource
{
    cocos2d::Size 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::Size designResolutionSize = cocos2d::CCSizeMake(480, 320); 

AppDelegate::AppDelegate() {

}

AppDelegate::~AppDelegate() 
{
}

//if you want a different context,just modify the value of glContextAttrs
//it will takes effect on all platforms
void AppDelegate::initGLContextAttrs()
{
    //set OpenGL context attributions,now can only set six attributions:
    //red,green,blue,alpha,depth,stencil
    GLContextAttrs glContextAttrs = {8, 8, 8, 8, 24, 8};

    GLView::setGLContextAttrs(glContextAttrs);
}

// If you want to use packages 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) {
        glview = GLViewImpl::create("Save The KoaLa");
        director->setOpenGLView(glview);
    }

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

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

    register_all_packages();

    // begin: Set the design 
    Size screenSize = glview->getFrameSize();
    auto fileUtils = FileUtils::getInstance();
    std::vector<std::string> searchPath;

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

    // 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);
        //director->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);
        //director->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);
        //director->setContentScaleFactor(smallResource.size.height / designResolutionSize.height);
    }
    director->setContentScaleFactor(screenSize.height / designResolutionSize.height);
    fileUtils->setSearchPaths(searchPath);
    // create a scene. it's an autorelease object
    auto scene = HelloWorld::createScene();

    // run
    director->runWithScene(scene);

    return true;
}

I used the cocos2dx v3.6, In the HellowWord Scence i add a background sprite and a close button.
I test the game on windows phone 8.1. My laptop screen size is 14inch (the visible size with the width is 1366 and the height is 768).

When i run the game the background not full my screen, and the close button put in right bottom conner of screen in the black area not inside the background visible area.
I wonder how to calculation the positions for the close button to it put inside the background image area and calculation the positions for other sprites to make sure it not in the black area.
I am sorry about my English.
The attach file show where i want to put the close button.


And i want other object in my game only put in the background image area. Not put in the black side.

Can any one help me. It better if i can make the background fullscreen because the background image is large enough.

  • If close button was added to background (background->addChild(closeBtn) …)
    closeBtn->setPosition(background->getContentSize().width - closeBtn->getContentSize().width / 2,
    closeBtn->getContentSize().height / 2);
  • If close button was added to layer
    closeBtn->setPosition(background->getBoundingBox().getMaxX() - closeBtn->getContentSize().width / 2,
    background->getBoundingBox().getMinY() + closeBtn->getContentSize().height / 2);

I do as you guide It work fine, thank you.

I meet anothe problem when calculation the position of sprites in the game. Example i want to put a tree sprite above the river. But it difficult to get the exactly the position.
I copy a part the bottom of background image, and save to png file riverheight.png

I intend to put the prite riverheight image at the bottom, and calculation the posistion of the tree from the top of the riverheight sprite.
But when i add the riverheight it to the game, the height of riverheight look higher than the bottom part of the background. event thought it the same high in design.

How to solve this problem. I think the background.png image and the riverheight.png was designed with the same scale, so when add to the game it must be fit. But the real it not fit. If it fit, i can put the tree on the top of riverheight to make sure the tree not put in the river or on the air. But i cannot resolve this.

Have some one give me some advice?

First you need get the right position, i often use photoshop to load images (background & tree image), then drag tree image to position u want, now u can calculate deltaY (from bottom of background to bottom of tree) and position y of tree will be:
treePosY = deltaY + tree->getContentsize().height / 2; (remember add tree to background).
Do the same for X.

I would like to get the height of riverheight.png to make the deltaY, because the height of riverheight.png in photoshop is same with the height of the bottom of the background (the river). But the problem is when add the riverheight.png to the background the height of the riverheight.png is higher than the bottom of background. How to make sure the height of riverheight.png is same of the bottom of background.

Can u post your code?

The code in AppDelegate.cpp

#include "AppDelegate.h"
#include "HelloWorldScene.h"
#include "SimpleAudioEngine.h"
using namespace CocosDenshion;
USING_NS_CC;

// for multi resolution support
typedef struct tagResource
{
    cocos2d::Size 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::Size designResolutionSize = cocos2d::CCSizeMake(480, 320); 


AppDelegate::AppDelegate() {

}

AppDelegate::~AppDelegate() 
{
}

//if you want a different context,just modify the value of glContextAttrs
//it will takes effect on all platforms
void AppDelegate::initGLContextAttrs()
{
    //set OpenGL context attributions,now can only set six attributions:
    //red,green,blue,alpha,depth,stencil
    GLContextAttrs glContextAttrs = {8, 8, 8, 8, 24, 8};

    GLView::setGLContextAttrs(glContextAttrs);
}

// If you want to use packages 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) {
        glview = GLViewImpl::create("Save The KoaLa");
        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.0 / 60);

    register_all_packages();

    // begin: Set the design 
    Size screenSize = glview->getFrameSize();
    auto fileUtils = FileUtils::getInstance();
    std::vector<std::string> searchPath;

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

    // 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);
        //director->setContentScaleFactor(largeResource.size.height / designResolutionSize.height);
        //director->setContentScaleFactor(screenSize.height / largeResource.size.height);
        //director->setContentScaleFactor(largeResource.size.height / screenSize.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);
        //director->setContentScaleFactor(mediumResource.size.height / designResolutionSize.height);
        //director->setContentScaleFactor(screenSize.height / mediumResource.size.height);
        //director->setContentScaleFactor(mediumResource.size.height / screenSize.height);
    }
    // if the frame's height is smaller than the height of medium resource size, select small resource.
    else
    {
        searchPath.push_back(smallResource.directory);
        //director->setContentScaleFactor(smallResource.size.height / designResolutionSize.height);
        //director->setContentScaleFactor(screenSize.height / smallResource.size.height);
        //director->setContentScaleFactor(smallResource.size.height / screenSize.height);
    }
    director->setContentScaleFactor(screenSize.height / designResolutionSize.height);
    //director->setContentScaleFactor(designResolutionSize.height / screenSize.height);
    fileUtils->setSearchPaths(searchPath);
    // 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. When comes a phone call,it's be invoked too
void AppDelegate::applicationDidEnterBackground() {
    Director::getInstance()->stopAnimation();

    // if you use SimpleAudioEngine, it must be pause
    SimpleAudioEngine::getInstance()->pauseBackgroundMusic();
}

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

    // if you use SimpleAudioEngine, it must resume here
    SimpleAudioEngine::getInstance()->resumeBackgroundMusic();
}

I put the background.pnd and tree.pnd and riverheight.png in a spritesheet
The Common.h file

#define kSpriteFile "spritesheet.plist"
#define kBackGroundFrame "background.png"
#define kRiverheight "riverheight.png"
#define kTree "tree.png"
#define kGroundHeight 55 

And below is the code in the init method in HelloWorldScene.cpp

#include "HelloWorldScene.h"
#include "Common.h"
#include "SimpleAudioEngine.h"
using namespace CocosDenshion;
USING_NS_CC;

// on "init" you need to initialize your instance
bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !Layer::init() )
    {
        return false;
    }
    auto glview = Director::getInstance()->getOpenGLView();
    Size screenSize = glview->getFrameSize();

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

    referenceSize = CCSizeMake(480, 320);

    // calculation the scale ratio
    scaleX = visibleSize.width / referenceSize.width;
    scaleY = visibleSize.height / referenceSize.height ;

    

    // preload resources for game
    SpriteFrameCache::getInstance()->addSpriteFramesWithFile(kSpriteFile);

    // add the background
    auto bg = Sprite::createWithSpriteFrameName(kBackGroundFrame);
    // original sprite resolution as designed for the 480x320 screen
  
    bg->Node::setPosition(Vec2(origin.x + visibleSize.width/2, bg->getBoundingBox().size.height / 2 + kGroundHeight*scaleY)); // subtract ground
   
    this->addChild(bg);



    auto riverheight = Sprite::createWithSpriteFrameName(kRiverheight);
   

    riverheight->setPosition(riverheight->getContentSize().width / 2, 
        riverheight->getContentSize().height / 2);
    bg->addChild(riverheight);
    // add the Tree 1
    tree1 = Sprite::createWithSpriteFrameName(kTree);
    tree1->setPosition(Vec2(60 * scaleX, riverheight->getContentSize().height/2 + tree1->getContentSize().height / 2));
    tree1->setLocalZOrder(2);
    bg->addChild(tree1);

    // add the Tree 2
    tree2 = Sprite::createWithSpriteFrameName(kTree);
    tree2->setPosition(Vec2(bg->getBoundingBox().getMaxX() - 65 * scaleX, riverheight->getContentSize().height / 2 + tree2->getContentSize().height / 2));
    
    tree2->setZOrder(2);
    bg->addChild(tree2);


    /////////////////////////////
    // 2. add a menu item with "X" image, which is clicked to quit the program
    //    you may modify it.

    // add a "close" icon to exit the progress. it's an autorelease object
    auto closeItem = MenuItemImage::create(
                                           "CloseNormal.png",
                                           "CloseSelected.png",
                                           CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));
    
   
    closeItem->setPosition(Vec2(bg->getBoundingBox().getMaxX() - closeItem->getContentSize().width / 2,
        bg->getBoundingBox().getMinY() + closeItem->getContentSize().height / 2));

    // create menu, it's an autorelease object
    auto menu = Menu::create(closeItem, NULL);
    menu->setPosition(Vec2::ZERO);
    this->addChild(menu, 1);

    /////////////////////////////
    // 3. add your codes below...

    // add a label shows "Hello World"
    // create and initialize a label
    

    std::stringstream ss;
    ss << riverheight->getContentSize().height  << " Width: " << screenSize.width << "; Height: " << screenSize.height;

    auto label = Label::createWithTTF(ss.str().c_str(), "fonts/Marker Felt.ttf", 24);

    // position the label on the center of the screen
    label->setPosition(Vec2(origin.x + visibleSize.width/2,
                            origin.y + visibleSize.height - label->getContentSize().height));

    // add the label as a child to this layer
    this->addChild(label, 1);

   
    
    return true;
}

your code run fine (size of river fit in background), u should check again resources size in 3 folder (iphone, ipad, ipadhd), the images u post is on HD size but u running in resolution 1366x768
P/S: i wonder why background 1536x2048 (w < h) but design resolution is 480x320 (w > h).

I have three different background.png and reverheight.png image with different size put in different folder

ipadhd folder

  1. background.png width 1536; height 2048
  2. riverheight.png width 78; height 222

ipad folder

  1. background.png width 768; height 1048
  2. riverheight.png width 42; height 111

iphone folder

  1. background.png width 640; height 1136
  2. riverheight.png width 28; height 108

And it sound i used the vertical image for horizontal screen. I order free game art, i am not have experience in design the game art.

I wonder if the code in the AppDelegate.cpp is correct? I debug and sê that it load the resource in the ipad folder.