4

So I am working on making a tower defense game and have the tile board working and able to place towers... to a degree.

When a player clicks a button above the grid, it will toggle on the tower placement and allow the player to place towers on a cell within the grid. However, each tower is also getting a circle ShapeNode around it to handle detection of a creep moving into firing range. This shape node shows up just fine around the tower once I place them.

The logic I used to place the towers is that it finds the node you touch (named "cell") and replaces it with a tower node. But if the tower node has the circle node attached and covering a cell next to it, I am unable to select the cell node below the circle.

How would I go about touching "through" the circle, or setting its fill space to be nothing so that I can access the cells below it?

I'm still learning sprite-kit as I go, so if there may be a simpler approach feel free to point me in the right direction too.

Klutch
  • 171
  • 1
  • 2
  • 13
  • Do you have the userInteractionEnabled property set to false for your circle node? If you have not changed this value it should be false by default. If it is false then that node should not receive touches. – nacross Apr 12 '14 at 07:23
  • This works, as they are no longer able to select the circle node, but it also does not rid the problem that the user is unable to select below the disabled node since it is still a node layered on top. – Klutch Apr 14 '14 at 18:26

3 Answers3

5

If you want find a node at a given point but not necessarily the deepest node you can use nodesAtPoint: available on SKNode including SKScene.

This allows you to find all child nodes of a scene, or node that intersect a given point. You could then filter this further to find the particular node you want, perhaps by filtering the array based on node class or node name.

A similar approach can be applied if looking for physics bodies at a particular point, using the method enumerateBodiesAtPoint:usingBlock: available in SKPhysicsWorld

It is worth noting that when using nodesAtPoint: the point is in the calling nodes coordinate system where as enumerateBodiesAtPoint:usingBlock: will always be in the scenes coordinate system.

nacross
  • 2,013
  • 2
  • 25
  • 37
  • This is awesome and works like a charm. Since I am still kind of new to SpriteKit, just wondering on some benchmarks. This was my first idea on how to create the tower placement and view logic, but can see that it is steadily beginning to lag as more things are added into the scene. – Klutch Apr 14 '14 at 18:07
  • Hi @nacross, could you take a look at my question? I'm having a similar issue with enumarateBodiesAtPoint. The bounty ends in 4 days. I tried all the enumerateBodiesAt methods and all are highly inaccurate and I don't know why http://stackoverflow.com/questions/24851309/get-a-list-of-nodes-in-an-specific-area – lisovaccaro Jul 25 '14 at 13:49
  • Hi @lisovaccaro, it seems to me that there is some kind of bug, I was quite certain this functionality has worked properly in the past but recently tried to use the same function in a new game and ran into issues. There seems to be multiple people experiencing this issue. You can see my comments here: https://devforums.apple.com/message/1003884#1003884 – nacross Jul 26 '14 at 15:25
3

Without seeing any of your relevant code it's hard to say for certain but from your description, I think your issue is with the shape node. Instead of using a shape node to handle your contacts, I would suggest you use the tower's physics body instead. You can still have a circular physics body for your towers without resorting to adding a whole new node. Removing the shape node will also resolve your cross touch interference.

sangony
  • 11,636
  • 4
  • 39
  • 55
  • I was also thinking that this might be the solution I was looking for, but am I able to make a Physics Body visible to the player, as I would like them to be able to see the radius the tower will fire at. – Klutch Apr 14 '14 at 18:05
  • 1
    To keep things simple you can consider using a couple of plain SKSpriteNodes. Each one can have a different size circle image in it (alpha setting of course). Make the zPosition low enough as not to interfere with any other touch items. – sangony Apr 14 '14 at 18:11
  • I like your ideas and help, they have been eye opening. I will have to play around with the sprites layering over the grid. Unless this sounds like a bad idea from the get go, I was also thinking about creating and destroying the circle sprite node every time the user "Selects" a tower, so there would only be one on screen at a time and not visible unless you tell it to show up. – Klutch Apr 14 '14 at 18:31
  • You can absolutely do that. It all boils down to how you design your code and want your game play to flow/function. Good luck and happy coding! – sangony Apr 14 '14 at 18:40
  • I have a similar sounding problem. I have a layer of tiles (SKSpriteNodes) and a layer of Shape Nodes for debugging. The layer of shape nodes was drawn offscreen onto an SKNode, and then converted to a texture.This is then overlaid on the game as the top z positioned node. What solution might you suggest given my situation? =] – Relequestual Jan 27 '16 at 22:47
0

TouchesBegan automatically detects all nodes at that position, you just have to loop through them.

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    for (UITouch *touch in touches)
    {
        CGPoint touchPoint = [touch locationInNode:self];
        NSArray *nodes = [self nodesAtPoint:touchPoint];

        if ([nodes count])
        {
            for (SKNode *node in nodes)
            {
                if ([node.name isEqualToString:@"cell"])
                {

                }
                else
                {

                }
            }
        }
    }
}

"self" being the node that touches began is called in (most likely your scene). You can also filter it by parent objects, for example; if you add all of your nodes to a parent node named "background", you can filter to only the children of the background node by replacing "self" with your background node (though this will result in it using the background nodes coordinates).

Placus Brutus
  • 93
  • 1
  • 4