6
votes

I have 2 SKSpriteNode:

  • a simple square (A)
  • the same square with a rotation (-45°) (B)

I need to check, at any time, if the center of another SKSpriteNode (a ball) is inside one of these squares. The ball and the squares have the same parent (the main scene).

override func update(_ currentTime: TimeInterval) {

    let spriteArray = self.nodes(at: ball.position)

    let arr = spriteArray.filter {$0.name == "square"}

    for square in arr {
        print(square.letter)

        if(square.contains(self.puck.position)) {
             print("INSIDE")
        }

    }
}

With the simple square (A), my code works correctly. The data are right. I know, at any time, if the CGPoint center is inside or outside the square.

But with the square with the rotation (B), the data aren't as desired. The CGPoint is detected inside as soon as it's in the square which the diamond-shape is contained.

demo

The SKSpriteNode squares are created via the level editor.

How can I do to have the correct result for the diamond-shape?

EDIT 1

Using

view.showsPhysics = true

I can see the bounds of all the SKSpriteNode with physicsBody. The bounds of my diamond-square is the diamond-square and not the grey square area.

 square.frame.size -> return the grey area
 square.size -> return the diamond-square

In the Apple documentation, func nodes(at p: CGPoint) -> [SKNode], the method is about node and not frame, so why it doesn't work?

1
Possibly, your frame is not using alpha mask? You could use .physicsBody and collisions to get the pinpoint accuracy of this. I don't have any code on hand, but you can do this for sure with SKPhysicsContactDelegate. stackoverflow.com/questions/27576149/…Fluidity
I tried to used .physicsBody and collision, but with this method, I know when the ball is in contact with the square. It isn't what I need. I'm interested in the contact with the center of the ball (CGPoint) and the square.It's not exactly the same thing.cmii
well, @cmi , I think you can still use at least .frame.intersects but you have to have the frame set up as an alpha mask. Right now, it's seeing the whole square (gray area) as a contact patch, whereas the alpha mask would only show the blue diamond area. If I'm wrong on that, then The only other way I can think of is just to do math and subtract out the 4 triangles (the gray areas) surrounding the blue diamond.Fluidity
@Fluidity please see my edit, indeed the frame of the diamond-square is the grey square. The node mask is the diamond-square.cmii
node.contains is not an SKPhysics method, and all that it does is check if it is inside the nodes coodinate system (the frame). If you know the angle, just figure out where the touch point lies relative to the center of the node, than apply a reverse angle rotation on it, then use this new point to check if it contains the nodeKnight0fDragon

1 Answers

2
votes

There are many ways to do it, usually I like to work with paths so , if you have a perfect diamond as you describe I would like to offer a different way from the comments, you could create a path that match perfectly to your diamond with UIBezierPath because it have the method containsPoint:

let f = square.frame
var diamondPath = UIBezierPath.init()
diamondPath.moveToPoint(CGPointMake(f.size.width-f.origin.x,f.origin.y))
diamondPath.addLineToPoint(CGPointMake(f.origin.x,f.size.height-f.origin.y))
diamondPath.addLineToPoint(CGPointMake(f.size.width-f.origin.x,f.size.height))
diamondPath.addLineToPoint(CGPointMake(f.size.width,f.size.height-f.origin.y))
diamondPath.closePath()
if diamondPath.containsPoint(<#T##point: CGPoint##CGPoint#>) {
   // point is inside diamond
}