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\$

1 Answer 1

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\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .