Pregunta

I am having a hard time trying to implement click handling in a simple isometric Sprite Kit game.

I have a Game Scene (SKScene) with a Map (SKNode) containing multiple Tile objects (SKSpriteNode).

Here is a screenshot of the map :

enter image description here

I want to be able to detect the tile the user clicked on, so I implemented mouseDown on the Tile object. Here is my mouseDown in Tile.m :

-(void)mouseDown:(NSEvent *)theEvent
{
    [self setColorBlendFactor:0.5];
}

The code seems to work fine but there is a glitch : the nodes overlap and the click event is detected on the transparent part of the node. Example (the rects have been added to illustrate the problem only. They are not used in the logic) :

enter image description here

As you can see, if I click on the top left corner of the tile 7, the tile 8 becomes transparent.

enter image description here

I tried something like getting all the nodes at click location and checking if click is inside a CGPath without success (I think there was something wrong in the coordinates).

So my question here is how to detect the click only on the texture and not on the transparent part? Or maybe my approach of the problem is wrong?

Any advice would be appreciated.

Edit : for anyone interested in the solution I finally used, see my answer here

¿Fue útil?

Solución

My solution for such a problem 'right now' is:

  • in your scene get all nodes which are in that position of your click, i.e.

    [myScene nodesAtPoint:[theEvent lactionInNode:myScene]]
    
  • don't forget to check if your not clicking the root of your scene something like that:

    if (![[myScene nodeAtPoint:[theEvent locationInNode:myScene]].name isEqual: @"MyScene"])
    

    then go through the Array of possible nodes and check the alpha of the Texture (NOT myNode.alpha)

  • if the alpha is 0.0f go to the next node of your Array
  • pick the first node which alpha is not 0.0f and return the nodes name
  • this way you can find your node (first) and save it, as the node you need, and kill the array, which you don't need anymore
  • than do what you want with your node
  • btw check if your new node you wish to use is nil after searching for its name. if thats true, just break out of your moveDown method

To get the alpha try this.

Mine looks something like that:

-(void)mouseDown:(NSEvent *)theEvent {
     /* Called when a mouse click occurs */
    if (![[self nodeAtPoint:[theEvent locationInNode:self]].name isEqual: self.name]]) {

        /* find the node you clicked */
        NSArray *clickedNodes = [self nodesAtPoint:[theEvent locationInNode:self]];

        SKNode *clickedNode = [self childNodeWithName:[clickedNodes getClickedCellNode]];

        clickedNodes = nil;

        /* call the mouseDown method of your Node you clicked to to node specific actions */
        if(clickedNode) {
           [clickedNode mouseDown:theEvent];
        }
        /* kill the pointer to your clicked node */
        clickedNode = nil;
    }
}

Otros consejos

For simple geometries like yours, a workaround would be to superimpose invisible SKShapeNodes on top of your diamonds, and watch for their touches (not for the skspritenode ones).

If this still does not work, make sure you create the SKPhysicsBody using the "fromPolygon: myNode.path!" option...

i have some problem with bugs [SKPhysicsWorld enumerateBodiesAtPoint]. But i found my solution and it will work for you too.

  1. Create tile path (your path 4 points)

  2. Catch touch and convert point to your tile node

  3. if touch point inside shape node - win!

CODE:

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event

selectedBuilding = nil;

NSArray *nodes = [farmWorldNode nodesAtPoint:point];
if (!nodes ||  nodes.count == 0 || nodes.count == 1) {
    return;
}

NSMutableArray *buildingsArray = [NSMutableArray array];

int count = (int)nodes.count;
for (int i = 0; i < count; i++) {

    SKNode *findedBuilding = [nodes objectAtIndex:i];

    if ([findedBuilding isKindOfClass:[FBuildingBaseNode class]]) {
        FBuildingBaseNode *building = (FBuildingBaseNode *)findedBuilding;

        CGPoint pointInsideBuilding = [building convertPoint:point fromNode:farmWorldNode];

        if ([building.colisionBaseNode containsPoint:pointInsideBuilding]) {
            NSLog(@"\n\n Building %@ \n\n ARRAY: %@ \n\n\n", building, findedBuilding);
            [buildingsArray addObject:building];
        }


    }
 }

 selectedBuilding = (FBuildingBaseNode *)[buildingsArray lastObject];


 buildingsArray = nil;
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top