Search code examples
cocoa-touchopengl-escocos2d-iphone

How can I ensure I still get correct touch inputs when my scene is offset?


How can I accept touch input beyond the scene's bounds, so that no matter what I set self.position to, touches can still be detected?

I'm creating a tile based game from Ray Winderlich on Cocos2d version 3.0. I am at the point of setting the view of the screen to a zoomed in state on my tile map. I have successfully been able to do that although now my touches are not responding since I'm out of the coordinate space the touches used to work on.

This method is called to set the zoomed view to the player's position:

-(void)setViewPointCenter:(CGPoint)position{
    CGSize winSize = [CCDirector sharedDirector].viewSizeInPixels;
    int x = MAX(position.x, winSize.width/2);
    int y = MAX(position.y, winSize.height/2);
    x = MIN(x, (_tileMap.mapSize.width * _tileMap.tileSize.width) - winSize.width / 2);
    y = MIN(y, (_tileMap.mapSize.height * _tileMap.tileSize.height) - winSize.height / 2);
    CGPoint actualPosition = ccp(x, y);

    CGPoint centerOfView = ccp(winSize.width/2, winSize.height/2);
    NSLog(@"centerOfView%@", NSStringFromCGPoint(centerOfView));
    CGPoint viewPoint = ccpSub(centerOfView, actualPosition);
    NSLog(@"viewPoint%@", NSStringFromCGPoint(viewPoint));

    //This changes the position of the helloworld layer/scene so that
    //we can see the portion of the tilemap we're interested in.
    //That however makes my touchbegan method stop firing 
    self.position = viewPoint;
}

This is what the NSLog prints from the method:

2014-01-30 07:05:08.725 TestingTouch[593:60b] centerOfView{512, 384}
2014-01-30 07:05:08.727 TestingTouch[593:60b] viewPoint{0, -832}

As you can see the y coordinate is -800. If i comment out the line self.position = viewPoint then the self.position reads {0, 0} and touches are detectable again but then we don't have a zoomed view on the character. Instead it shows the view on the bottom left of the map.

Here's a video demonstration.

How can I fix this?

Update 1

Here is the github page to my repository.

Update 2 Mark has been able to come up with a temporary solution so far by setting the hitAreaExpansion to a large number like so:

self.hitAreaExpansion = 10000000.0f;

This will cause touches to respond again all over! However, if there is a solution that would not require me to set the property with an absolute number then that would be great!


Solution

  • -edit 3-(tldr version): setting the contentsize of the scene/layer to the size of the tilemap solves this issue:

    [self setContentSize: self.tileMap.contentSize];


    original replies below:

    You would take the touch coordinate and subtract the layer position.

    Generally something like:

    touchLocation = ccpSub(touchLocation, self.position);
    

    if you were to scale the layer, you would also need appropriate translation for that as well.

    -edit 1-:

    So, I had a chance to take another look, and it looks like my 'ridiculous' number was not ridiculous enough, or I had made another change. Anyway, if you simply add

    self.hitAreaExpansion = 10000000.0f; // I'll let you find a more reasonable number 
    

    the touches will now get registered.

    As for the underlying issue, I believe it to be one of content scale that is not set correctly, but again, I'll now leave that to you. I did however find out that when looking through some of the tilemap class, that tilesize is said to be in pixels, not points, which I guess is somehow related to this.

    -edit 2-:

    It bugged me with the sub-optimal answer, so I looked a little further. Forgive me, I hadn't looked at v3 until I saw this question. :p

    after inspecting the base class and observing the scene/layer's value of:

    - (BOOL)hitTestWithWorldPos:(CGPoint)pos;
    

    it became obvious that the content size of the scene/layer was being set to the current view size, which in the case of an iPad is (1024, 768)

    The position of the layer after the setViewPointCenter call is fully above the initial view's position, hence, the touch was being suppressed. by setting the layer/scene contentSize to the size of the tilemap, the touchable area is now expanded over the entire map, which allows the node to process the touch.