Reputation: 171
I am trying to get my bounding box to work properly, but keep failing when the Map gets scaled. I could really use some assistance here as this has been the bane of my existence.
In a nutshell, I have 1 scene, 2 layers. One layer is a HUD and another is a map. The map is a layer that is rendered by tiling 100 images from a large sliced image. I scale these images to the given spacial scaling and they look great at all sizes. The problem occurs when I try to set a bounding box for the Width/Height of the Map while scaled. I am unable to get it to lock properly (Only right boundary and top boundary, left and bottom work fine). As an FYI, this will work beautifully when Scale is equal to 1, but as soon as I scale, everything gets awkward, most likely due to the changing X,Y of the Map Tiles when I scale the Sprite.
Please help as I don't know what I am doing wrong and cannot wrap my head around the spaces... Here is some code to help:
bool MapLayer::init()
{
// Super init
if ( !CCLayer::init() )
{
return false;
}
// We need to enable touches for this layer
setTouchEnabled( true );
// Set the Anchor Point
setAnchorPoint(ccp(0,0));
// We need to initialize the Map Textures for Rendering (Break up further for performance)
for(int row = 0; row < NUM_MAP_FRAME_ROWS; row++)
{
for(int col = 0; col < NUM_MAP_FRAME_COLS; col++)
{
char imageFrameFile[25] = "";
int imageFrame = ((row * 10) + col) + 1;
// Create the String to load the image frame
sprintf(imageFrameFile, "map_%02d.png", imageFrame);
// Create the Sprite
map[row][col] = CCSprite::create(imageFrameFile);
// Add the Sprite to the Scene
this->addChild(map[row][col], 0);
}
}
// Render the Map at the default Scale (Show entire Map)
renderMap();
// Position the Viewport to the center of the map
setPosition(ccp(0 - (((map[9][9]->getPosition().x + map[9][9]->getContentSize().width) / 2.0f) - ((map[9][9]->getContentSize().width / 2.0f))),
0 - ((map[0][0]->getPosition().y / 2.0f) - ((map[0][0]->getContentSize().height / 2.0f)))));
return true;
}
void MapLayer::renderMap(void)
{
int overlapBuffer = 0;
float spritePositionX = 0.0f;
float spritePositionY = 0.0f;
// Reset our Boundary variables
rightBoundary = 0;
topBoundary = 0;
// Display the Tile-based Map
for(int row = NUM_MAP_FRAME_ROWS - 1; row >= 0; row--)
{
// Reset the starting position
spritePositionX = 0.0f;
for(int col = 0; col < NUM_MAP_FRAME_COLS; col++)
{
CCSprite * pSprite = map[row][col];
CCSize spriteContentSize = pSprite->getContentSize();
float spriteWidth = spriteContentSize.width;
float spriteHeight = spriteContentSize.height;
// Position the Sprite by using the previous Sprite's location
pSprite->setScale(getScale());
pSprite->setAnchorPoint(ccp(0, 0));
pSprite->setPosition(ccp(spritePositionX, spritePositionY));
// Increment for the next Sprite Position
spritePositionX += (float)(spriteWidth * pSprite->getScale()) - overlapBuffer;
// Increment if this is the last column item
if(col == (NUM_MAP_FRAME_COLS - 1))
{
// Increment for the next Sprite Position
spritePositionY += (float)(spriteHeight * pSprite->getScale()) - overlapBuffer;
}
}
}
// TEST CODE
CCPoint boundaries = convertToWorldSpace(ccp(spritePositionX, spritePositionY));
rightBoundary = boundaries.x;
topBoundary = boundaries.y;
// TEST CODE
}
void MapLayer::scale(float newScale, CCPoint scaleCenter)
{
// scaleCenter is the point to zoom to..
// If you are doing a pinch zoom, this should be the center of your pinch.
// Get the original center point.
CCPoint oldCenterPoint = ccp(scaleCenter.x * getScale(), scaleCenter.y * getScale());
// Set the scale.
setScale(newScale);
// Get the new center point.
CCPoint newCenterPoint = ccp(scaleCenter.x * getScale(), scaleCenter.y * getScale());
// Then calculate the delta.
CCPoint centerPointDelta = ccpSub(oldCenterPoint, newCenterPoint);
// Now adjust your layer by the delta.
setPosition(ccpAdd(getPosition(), centerPointDelta));
// Render the Map to the new Scale
renderMap();
}
void MapLayer::checkBoundingBox(void)
{
// Grab the display size from the director
CCSize winSize = CCDirector::sharedDirector()->getWinSize();
cocos2d::CCLog("************************************");
cocos2d::CCLog("WORLD Position-X: %f", convertToWorldSpace(getPosition()).x);
cocos2d::CCLog("WORLD Position-Y: %f", convertToWorldSpace(getPosition()).y);
cocos2d::CCLog("Position-X: %f", (getPosition()).x);
cocos2d::CCLog("Position-Y: %f", (getPosition()).y);
// cocos2d::CCLog("Map[0][0]-X: %f", convertToWorldSpace(map[0][0]->getPosition()).x);
// cocos2d::CCLog("Map[0][0]-Y: %f", convertToWorldSpace(map[0][0]->getPosition()).y);
// cocos2d::CCLog("Map[9][9]-X: %f", convertToWorldSpace(map[9][9]->getPosition()).x);
// cocos2d::CCLog("Map[9][9]-Y: %f", convertToWorldSpace(map[9][9]->getPosition()).y);
cocos2d::CCLog("WinSize Width: %f", winSize.width);
cocos2d::CCLog("WinSize Height: %f", winSize.height);
/*
* Check if we have scaled beyond our limits
*/
// Check the Width
if(getPositionX() >= 0)
{
// Set the Position
setPositionX(0);
}
// Check the Height
if(getPositionY() >= 0)
{
// Set the Position
setPositionY(0);
}
setPosition(ccpClamp(getPosition(),
ccp(-(rightBoundary - winSize.width),
-(topBoundary - winSize.height)),
ccp(0,0)));
}
void MapLayer::ccTouchesMoved( CCSet *pTouches, CCEvent *pEvent )
{
// Verify that we are Panning the Map
if(pTouches->count() == 1)
{
// Grab the touch to handle the pan
CCTouch * panTouch = (CCTouch *)pTouches->anyObject();
// Get the touch and previous touch
CCPoint touchLocation = panTouch->getLocationInView();
CCPoint previousLocation = panTouch->getPreviousLocationInView();
// Get the distance for the current and previous touches.
float currentDistanceX = touchLocation.x - previousLocation.x;
float currentDistanceY = touchLocation.y - previousLocation.y;
// Set new position of the layer
setPosition(ccp(getPosition().x + currentDistanceX, getPosition().y - currentDistanceY));
}
// Verify that we are Zooming the Map
else if (pTouches->count() == 2)
{
// Grab Touch One
CCTouch * touchOne = (CCTouch *)pTouches->anyObject();
// Don't grab the same Touch
pTouches->removeObject(touchOne);
// Grab Touch Two
CCTouch * touchTwo = (CCTouch *)pTouches->anyObject();
// Get the touches and previous touches.
CCPoint touchLocationOne = touchOne->getLocationInView();
CCPoint touchLocationTwo = touchTwo->getLocationInView();
CCPoint previousLocationOne = touchOne->getPreviousLocationInView();
CCPoint previousLocationTwo = touchTwo->getPreviousLocationInView();
// Get the distance for the current and previous touches.
float currentDistance = sqrt(pow(touchLocationOne.x - touchLocationTwo.x, 2.0f) +
pow(touchLocationOne.y - touchLocationTwo.y, 2.0f));
float previousDistance = sqrt(pow(previousLocationOne.x - previousLocationTwo.x, 2.0f) +
pow(previousLocationOne.y - previousLocationTwo.y, 2.0f));
// Get the delta of the distances.
float distanceDelta = currentDistance - previousDistance;
// Next, position the camera to the middle of the pinch.
// Get the middle position of the pinch.
CCPoint pinchCenter = ccpMidpoint(touchLocationOne, touchLocationTwo);
// Then, convert the screen position to node space... use your game layer to do this.
pinchCenter = convertToNodeSpace(pinchCenter);
// Finally, call the scale method to scale by the distanceDelta, pass in the pinch center as well.
// Also, multiply the delta by PINCH_ZOOM_MULTIPLIER to slow down the scale speed.
scale(getScale() + (distanceDelta * PINCH_ZOOM_MULTIPLIER), pinchCenter);
}
/*
* Verify that the new position is within our bounding box, otherwise lock it
*/
checkBoundingBox();
}
Upvotes: 1
Views: 3358
Reputation: 2267
You can calculate accurate bounding box relative to position in parent node, scale and anchor point. You can do something like this:
CCSize size = CCSizeMake(node.width * node.scaleX, node.height * node.scaleY);
CCPoint origin = ccp(node.position.x - (size.width * node.anchorPoint.x), node.position.y - (size.height * node.anchorPoint.y));
CCRect boundingBox = (CCRect){origin, size};
Upvotes: 1