1
\$\begingroup\$

I'm trying to make a tower defense game and it can zoom in/out and scrolling over my world map. How to scroll over the game and how to restrict it not to show outside of my map(black area).

At below I scroll over the map by using CCCamera but I don't know how I can restrict it.

CCPoint tap = touch->getLocation();
CCPoint prev_tap = touch->getPreviousLocation();
CCPoint sub_point = tap - prev_tap;

float xNewPos, yNewPos;
float xEyePos, yEyePos, zEyePos;
float cameraPosX, cameraPosY, cameraPosZ;

// First we get the current camera position.
GameLayer->getCamera()->getCenterXYZ(&cameraPosX, &cameraPosY, &cameraPosZ);
GameLayer->getCamera()->getEyeXYZ(&xEyePos, &yEyePos, &zEyePos);

// Calculate the new position
xNewPos = cameraPosX - sub_point.x;
yNewPos = cameraPosY - sub_point.y;

GameLayer->getCamera()->setCenterXYZ(xNewPos, yNewPos, cameraPosZ);
GameLayer->getCamera()->setEyeXYZ(xNewPos, yNewPos, zEyePos);

And for zooming I used such code:

GameLayer->setScale(GameLayer->getScale() + 0.002); //zooming in
\$\endgroup\$
6
\$\begingroup\$

EDIT : Multi-resolution bug fixed.

Here is my solution for you:

First of all Let's sync some assumption:

  1. We have a layer ( Let's say MapLayer ) that includes game map background ( Let's name it map) and its content( map-related objects, e.g. towers and enemies).

  2. I didn't use CCCamera. I tried to scale and move MapLayer instead.

  3. bottom-left and top-right position of map before layer scale and after. No Description

Here we go..

1. MapLayer.h changes

class MapLayer : public cocos2d::CCLayer
{
private:
    // this sprite define your visible area boundary
    CCSprite* map;
    CCPoint visible, origin;
    // 'currentCenter' is based on nonScaled-Layer-Size.
    // [0,0] => [map.width , map.height]
    // = keep pixle of GameLayer that you want to be shown in middle of screen
    // = (visible.x/2, visible.y/2) means you want to move map as left/bottom as possible
    CCPoint currentCenter;

    // Correct currentCenter value that MoveCenterTo don't show ouside of map
    CCPoint CorrectCenterPoint( CCPoint currentCenter, float layerScale );

    // Move this layer in order to make `center` point center of screen
    void MoveCenterTo(CCPoint center, float layerScale);
};

2. MapLayer.cpp changes

bool GameLayer::init()
{
    if (!CCLayer::init())
    {
        return false;
    }
    visible = CCDirector::sharedDirector()->getVisibleSize();
    origin = CCDirector::sharedDirector()->getVisibleOrigin();
    // We need to set GameLayer position according to left-bottom point
    this->setAnchorPoint(ccp(0, 0));

    // IMPORTANT: We assume Left-Bottom of map is in (0,0) of GameLayer
    map->setPosition(ccp(map->getContentSize().width /2,
                    map->getContentSize().height /2));
    currentCenter =  ccp(visible.x / (2 * this->getScale()),
                         visible.y / (2 * this->getScale()));

    ....
}

// presume : `center` is based on nonScaled-layer-size:  [0,0] => [map.width, map.height]
CCPoint MapLayer::CorrectCenterPoint( CCPoint center, float layerScale )
{
    CCPoint scaledCenter = center * layerScale;

    // mapSize = in-game size of map. ( Don't forget to multiply this to map->getScale() if you had scaled map)
    CCSize mapSize = background->getContentSize() * background->getScale();
    float scale = this->getScale();

    scaledCenter.x = max(scaledCenter.x, visible.x/ 2);
    scaledCenter.x = min(scaledCenter.x, mapSize.width*scale - visible.x/2);
    scaledCenter.y = max(scaledCenter.y, visible.y/ 2);
    scaledCenter.y = min(scaledCenter.y, mapSize.height*scale - visible.y/2);

    return (scaledCenter/layerScale);
}

// presume : `center` is based on nonScaled-layer-size:  [0,0] => [map.width, map.height]
void MapLayer::MoveCenterTo(CCPoint center, float layerScale)
{
    // Convert nonScaled-layer-size to scaled-layer-size
    // [0,0] => [0,0] ||| [map.width, map.height] => [map.width * S , map.height * S]
    CCPoint scaledCenter = center * layerScale;

    scaledCenter.x = -scaledCenter.x + visible.x / 2 + origin.x;
    scaledCenter.y = -scaledCenter.y + visible.y / 2  + origin.y;

    this->setPosition(scaledCenter);
}

void MapLayer::ccTouchesMoved(CCSet* pTouches, CCEvent* pEvent)
{
    CCTouch* touch = (CCTouch*)pTouches->anyObject();
    CCPoint location = touch->getLocation();
    CCPoint deltaMove = touch->getDelta();

    // 100 unit move of a 2x layer, feels 200 unit move. So we calculate Delta as 1/2 to match layer move with touch-move.
    // Therefore, if we move 100unit touch, we need to move 50unit the layer to show 100unit move in screen.
    currentCenter = currentCenter - deltaMove / this->getScale();
    currentCenter = CorrectCenterPoint(currentCenter, this->getScale() ) ;
    MoveCenterTo( currentCenter , this->getScale());

    // Processing Touches :
    CCPoint scaledCenter = currentCenter*this->getScale() ;
    CCPoint offsetFromCenterOfScreen = location - (visible / 2)  - origin  ;
    // 100 unit move of a 2x layer, feels 200 unit move. So we calculate Delta as 1/2 to match layer move with touch-move.
    // Therefore, if we move 100unit touch, we need to move 50unit the layer to show 100unit move in screen.
    CCPoint RealTouchLocationinLayer  = (scaledCenter + offsetFromCenterOfScreen)/this->getScale() ;
}

** Zoom In/Out ** I don't enter in detail for zoom and just give you a working clue :

void MapLayer::zoomIn(CCTouch * touch)
{
    CCSize screenSize = CCDirector::sharedDirector()->getWinSize();
    // the offset of zoom-point and center of screen
    // for simplification I used touch location as zoom-point and make it center of camera for test.
    currentCenter = currentCenter + ( touch->getLocation() - visible/2 ) ;
    // here we increase scale some constant value in order to test
    this->setScale(this->getScale() + 0.5);
}

Hope Helps you. ;)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.