Detect CCSprite touch in small distance between them - cocos2d-iphone

I have some small sprites (width/height) in a CCLayer and i want to detect which of them is touched. I use the following code.
- (BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
CGPoint touchLocation = [self convertTouchToNodeSpace:touch];
CGRect rect = sprite.boundingBox;
// i am doing this because of the small size of the sprites,
// so it would be more easy to detect if a sprite is taped and then move it.
if (rect.size.width > rect.size.height) {
rect.size.width *= 2.5;
rect.size.height *= 5;
rect.origin.y -= rect.size.height / 2;
} else {
rect.size.width *= 5;
rect.size.height *= 2.5;
rect.origin.x -= rect.size.width / 2;
}
CCSprite *s = nil;
for (CCSprite *sprite in [self children]) {
if (CGRectContainsPoint(rect, touchPoint)) {
s = sprite;
break;
}
}
if (s != nil) {
// do something here
}
return YES;
}
All works fine except when two sprites is very close (not overlap) to each other. Then because of the small distance between them, the wrong sprite is detected.
Any idea how could i correct it?

If you are trying to enlarge the rects keeping their center where it originally is, you should use:
rect.origin.y -= (rectFinalHeight - rectOriginalHeight) / 2;
and
rect.origin.y -= (rectFinalWidth - rectOriginalWidth) / 2;
which would be:
if (rect.size.width > rect.size.height) {
rect.size.width *= 2.5;
rect.size.height *= 5;
rect.origin.y -= rect.size.height * 4 / 2;
} else {
rect.size.width *= 5;
rect.size.height *= 2.5;
rect.origin.x -= rect.size.width * 4 / 2;
}
IMO, you should adjust the rect origin along both axis in both cases (and not only in one case along the x axis and in the other along the y axis), otherwise your rect will not be centered. But you understand better what you are trying to do, here...

Related

Zooming towards center of Camera on 2d Plane

Once again, camera zooming on a 2D-Plane. I searched a lot and know that there are similar questions, but I am obviously way too stupid to apply what I was able to find.
Basically I multiply the distance of all elements to the origin by mouseDelta, which is a double between 0.5 and 1. works fine for all elements, but since the anchor of the camera (camX, camY) are the upper left corner of the camera, the objects in the focus of the cam change their position in relation to the focus. I want to scroll "towards" the focus. Here is what I got, but it behaves really weird:
camX and camY, as mentioned, are the coordinates for the upper left of the cam.
mouseDelta is the zoom-level thats stored globally and is changed by each wheel-event.
screenX is the width of the screen/window (fullscreen anyways)
screenY is the height of the screen/window
if (newEvent.type == sf::Event::MouseWheelMoved) //zoom
{
mouseDelta += ((double)newEvent.mouseWheel.delta)/20;
if (mouseDelta > 1) { mouseDelta = 1; }
else if (mouseDelta < 0.5) { mouseDelta = 0.5; }
//resize graphics
for (int i = 0; i < core->universe->world->nodes.size(); i++) {
core->universe->world->nodes.at(i).pic->setSize(mouseDelta);
}
for (int i = 0; i < core->universe->world->links.size(); i++) {
core->universe->world->links.at(i).pic->setSize(mouseDelta);
}
camX = (camX + screenX/2) - (camX + screenX/2)*mouseDelta;
camY = (camY + screenY/2) - (camY + screenY/2)*mouseDelta;
}

cocos2dx inverse scaling a layer

I have a CCLayer which holds all my game objects and i have implemented scaling and scrolling. To make sure that you can't scroll out of bounds i am calculating a rect that represents the screen and use it to check if the rect is within bounds. The problem is that my calculations are wrong. The layer is scaled by a scaleFactor like so:
world->setScale(scaleFactor);
and then i calculate the scroll rect:
float scrollWidth = winSize.width * ( 1 / scaleFactor); // winSize is the size of the screen (1280x
float scrollHeight = winSize.height * ( 1 / scaleFactor);
if(scrollRect.l < 0) scrollRect.l = 0;
if(scrollRect.l + scrollWidth > levelWidth) scrollRect.l -= (scrollRect.l + scrollWidth - levelWidth);
scrollRect.r = scrollRect.l + scrollWidth;
world->setPosition(-scrollRect.l, -scrollRect.b);
(the scale factor value is between 1.0 and 0.5)
This sort of works but only when the layer is zoomed out to the max or zoomed in to the minimum but when scaleFactor isn't MAX/MIN it is wrong (there is some left space).
What am i doing wrong? I've also tried changing the anchor points (they are currently set to 0,0) but without any success.
You can do this whatever your scale factor...
here _tileMap is your world
//Code for getting difference b/w two position
CCTouch *fingerOne = (CCTouch*)touchArray->objectAtIndex(0);
CCPoint newTouchLocation = fingerOne->getLocationInView();
newTouchLocation = CCDirector::sharedDirector()->convertToGL(newTouchLocation);
newTouchLocation=_tileMap->convertToNodeSpace(newTouchLocation);
CCPoint oldTouchLocation = fingerOne->getPreviousLocationInView();
oldTouchLocation = CCDirector::sharedDirector()->convertToGL(oldTouchLocation);
oldTouchLocation = _tileMap->convertToNodeSpace(oldTouchLocation);
//get the difference in the finger touches when the player was dragging
CCPoint difference = ccpSub(newTouchLocation, oldTouchLocation);
CCPoint ASD=ccpAdd(_tileMap->getPosition(), ccpMult(difference, _tileMap->getScale()));
CCPoint bottomLeft =ASD;
// Bounding Box....
if (bottomLeft.x >0) {
bottomLeft.x = 0;
}
if (bottomLeft.y>0) {
bottomLeft.y = 0;
}
if (bottomLeft.x < -(mapWidth*_tileMap->getScale() - _screenSize.width)) {
bottomLeft.x = -(mapWidth*_tileMap->getScale()- _screenSize.width);
}
if (bottomLeft.y <-(mapHieght*_tileMap->getScale() - _screenSize.height)) {
bottomLeft.y = - (mapHieght*_tileMap->getScale() - _screenSize.height);
}
_tileMap->setPosition(bottomLeft);
I hope this may help you..

Cocos2d - Moving the Layer/view based on ball movement

My game world space is a rectangle of 4 screen sizes (2 rows x 2 columns )
I am moving the camera based on the movement of a ball. The ball position is set according to the physics engine.
I notice that, sometimes the ball changes direction slightly as the camera is moving to follow the ball.
Initially I thought it was sort of an illusion based on the movement of the camera, but when I draw a path, the line is of crooked at times.
I used some of the code behind the CCFollow implementation code (not CCFollow itself, but the code behind it), for following the ball
(I had issues with CCfollow)
Below is my Tick() function:
————————————————–
- (void)tick:(ccTime) dt {
bool ballMov = false;
CGPoint pos;
CCSprite *ballData;
dt = 1.0 / 60.0f;
world->Step(dt,8, 3 );
for(b2Body *b = world->GetBodyList(); b; b=b->GetNext()) {
if (b->GetUserData() != NULL) {
CCSprite *tempData = (CCSprite *)b->GetUserData();
if (tempData == ball){
ballData = tempData;
ballMov = true;
ballData.position = ccp(b->GetPosition().x * PTM_RATIO, b->GetPosition().y * PTM_RATIO);
}
}
}
//
// Move the layer so we can keep looking at the travelling ball
// Code is similar to CCFollow code with some adjustments
pos = ballPosition;
if(ballMov){
#define CLAMP(x,y,z) MIN(MAX(x,y),z)
pos = ccp(CLAMP(pos.x,leftBoundary,rightBoundary), CLAMP(pos.y,bottomBoundary,topBoundary));
#undef CLAMP
pos = ccpSub(pos, halfScreenSize );
pos.x = - pos.x ;
pos.y = - pos.y ;
//calculate delta move towards the new position
if(gunShot){
CGPoint moveVect;
CGPoint oldPos = [self position];
double dist = ccpDistance(pos, oldPos);
if (dist > 1){
moveVect = ccpMult(ccpSub(pos,oldPos),0.4); //0.05 is the smooth constant.
oldPos = ccpAdd(oldPos, moveVect);
pos = oldPos;
}
}
}
//move towards the ball
[self setPosition:(pos)];
lastPosition = pos;
ballPosition = CGPointMake([ball body]->GetPosition().x * PTM_RATIO, [ball body]->GetPosition().y * PTM_RATIO );
//if ball is out of gameworld space then reset
if (!CGRectContainsPoint ( outerBoundaryRect, ballPosition)){
ball.visible = false ;
[self recreateBody]; //Ball is out of Gameworld, recreate it at the default position
}
}

Cocos2D - adding support for normalized position

I want cocos2d to use the nodes' position as a normalized factor when coordinates are less than 1, and otherwise when coordinates are greater than 1, I want cocos2d to treat coordinates as it would normaly do
For instace position (1,1) would locate the node at the top-right corner of the screen, while (.5,.5) would locate the node on the center of the screen.
I was thinking on rewriting the code just where cocos2d pass on the coordinates to the vertex buffer like the following pseudo code
if (Position.x <= 1)
BufferPosition.x = Screen.Width * Position.x;
else
BufferPosition.x = Position.x;
if (Position.y <= 1)
BufferPosition.y = Screen.Height * Position.y;
else
BufferPosition.y = Position.y;
So my question is where would you put this code so that cocos2d works that way with no problem, or what approach would you take istead if it's different from the shown above
Update
I changed every single call/assignment of positionInPixels_ in the entire cocos2d library for positionInPixels
This means that each time, the corresponding getter and setter is caled instead of the CCNode's positionInPixels_ member variable.
Then I changed setPosition, and setPositionInPixels in CCNode.m like this
setPosition
-(void) setPosition: (CGPoint)newPosition
{
CGSize winSize = [[CCDirector sharedDirector] winSize];
position_ = newPosition;
if (position_.x <= 1.0) {
positionInPixels_.x = position_.x * winSize.width;
}
else
{
if( CC_CONTENT_SCALE_FACTOR() == 1 )
positionInPixels_.x = position_.x;
else
positionInPixels_.x = newPosition.x * CC_CONTENT_SCALE_FACTOR();
}
if (position_.y <= 1.0) {
positionInPixels_.y = position_.y * winSize.height;
}
else
{
if( CC_CONTENT_SCALE_FACTOR() == 1 )
positionInPixels_.y = position_.y;
else
positionInPixels_.y = newPosition.y * CC_CONTENT_SCALE_FACTOR();
}
isTransformDirty_ = isInverseDirty_ = YES;
#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
isTransformGLDirty_ = YES;
#endif
}
setPositionInPixels
-(void) setPositionInPixels:(CGPoint)newPosition
{
CGSize winSize = [[CCDirector sharedDirector] winSize];
positionInPixels_ = newPosition;
if (position_.x <= 1.0) {
position_.x = positionInPixels_.x / winSize.width;
}
else
{
if( CC_CONTENT_SCALE_FACTOR() == 1 )
position_.x = positionInPixels_.x;
else
position_.x = newPosition.x * 1/CC_CONTENT_SCALE_FACTOR() / winSize.width;
}
if (position_.y <= 1.0) {
position_.y = positionInPixels_.y / winSize.height;
}
else
{
if( CC_CONTENT_SCALE_FACTOR() == 1 )
position_.y = positionInPixels_.y;
else
position_.y = newPosition.y * 1/CC_CONTENT_SCALE_FACTOR() / winSize.height;
}
isTransformDirty_ = isInverseDirty_ = YES;
#if CC_NODE_TRANSFORM_USING_AFFINE_MATRIX
isTransformGLDirty_ = YES;
#endif
}
This is working so far, even when using normalized coordinates with CCActionInterval
So you really think that this variant is the right one? To change the whole framework instead of just adding one method to the CCNode's extension? I think you will see a lot of problem with positioning very soon.
Also it is not good, that (1.f, 1.f) is always have coordinates (screenWidth, screenHeight). You will have a lot of troubles with small nodes. IMHO if you need such positioning, it is much more appropriate to connect this "factor" coordinates to the node's parent size.
Just do restore cocos2d framework and add your own extension to your project. For example,
#interface CCNode(FactorPositioning)
- (void) setPositionFactor:(CGFloat) factor;
#end
#implementation CCNode(FactorPositioning)
- (void) setPositionFactor:(CGFloat) factor
{
assert(self.parent != nil);
// do your calculations relatieve to the parent's content size
}
#end

Cocos2d Accelerometer Landscape

Im having trouble getting the accelerometer to work correctly in landscape mode with cocos2d. Everything works perfectly in portrait, but cant figure out how to flip it to correctly work in landscape. here is my code:
-(void) accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
float deceleration = 0.0f;
float sensativity = 15.0f;
float maxVelocity = 100;
playerVelocity.x = playerVelocity.x * deceleration + acceleration.x * sensativity;
playerVelocity.y = playerVelocity.y * deceleration + acceleration.y * sensativity;
if (playerVelocity.x > maxVelocity)
{
playerVelocity.x = maxVelocity;
}
else if (playerVelocity.x < - maxVelocity)
{
playerVelocity.x = - maxVelocity;
}
if (playerVelocity.y > maxVelocity)
{
playerVelocity.y = maxVelocity;
}
else if (playerVelocity.y < - maxVelocity)
{
playerVelocity.y = - maxVelocity;
}
}
-(void) update:(ccTime) delta
{
CGPoint pos = player.position;
pos.x += playerVelocity.x;
pos.y += playerVelocity.y;
CGSize screeSize = [[CCDirector sharedDirector]winSize];
float imageHalf = [player texture].contentSize.width * 0.5f;
float leftLimit = imageHalf;
float rightLimit = screeSize.width - imageHalf -20;
float bottomLimit = imageHalf;
float topLimit = screeSize.height - imageHalf - 20;
if (pos.y < bottomLimit) {
pos.y = bottomLimit;
}
else if (pos.y > topLimit){
pos.y = topLimit;
}
if (pos.x < leftLimit) {
pos.x = leftLimit;
}
else if (pos.x > rightLimit){
pos.x = rightLimit;
}
player.position = pos;
}
In landscape orientation, the accelerometer x and y values are flipped. Furthermore, in interface orientation landscape right, x is negative. In landscape left, y is negative:
// landscape right:
position.x = -acceleration.y
position.y = acceleration.x
// landscape left:
position.x = acceleration.y
position.y = -acceleration.x
Look at answer in this question
Best way for accelerometer usage in cocos2d game.