2

I'm having trouble using SKNode.contains() on scaled sprites, and I can't figure out what important concept I'm missing.

I have "gremlin" sprites that move around inside a "jail" sprite. Gremlins can go through walls sometimes, and I want to know when they've left the jail. I thought I could simply use jail.contains(gremlin) (or some variant), perhaps with a bit of math done on it to get the scale right, but no joy. I can't seem to figure out which sprite or which frame, or which size, or what kind of transform, or whatever, to use with contains().

I've read quite a bit about coordinate systems, frames and bounds, hit testing, scaling, frames, sizes, origins, anchor points, everything I've been able to find. I've read this SO question, and this, this, this, and this, and quite a few others.

I'm missing something. Here's some stripped-down code that shows the problem I'm having. This is the cleaned-up version. I've tried every permutation of convertPoint(), shrinking and growing size, shrinking and growing scale, changing who is whose parent, and various other desperate attempts to understand. No luck.

In this sample code, just to get my head around the problem, I'd like to control which of the big rectangles is to serve as the container for the small ones. The way it's set right now, I'm trying to get the little rectangles outside the red rectangle to be gray. As you can see, it seems that the red rectangle's hit-test area is the same size as that of the blue rectangle. That is, everything is green except the ones out at the very edges of the blue rectangle.

Help!

Brain-deadness

class GameScene: SKScene {
    static var shared: GameScene?

    var bottomSprite: SKSpriteNode!
    var middleSprite: SKSpriteNode!
    var setupComplete = false
    var spriteTexture: SKTexture?
    var textureAtlas: SKTextureAtlas?
    var topSprite: SKSpriteNode!

    func getMarkerColor(outerSprite: SKSpriteNode, innerSprite: SKSpriteNode) -> NSColor {
        return outerSprite.frame.contains(innerSprite.frame) ? .green : .gray
    }

    override func didMove(to view: SKView) {
        GameScene.shared = self
        spriteTexture = SKTexture(imageNamed: "debugRectangle")
    }

    func drawContainerSprite(parent: SKNode, scale: CGFloat, color: NSColor) -> SKSpriteNode {
        let sprite = SKSpriteNode(texture: spriteTexture)

        sprite.color = color
        sprite.colorBlendFactor = 1.0
        sprite.anchorPoint = CGPoint(x: 0.5, y: 0.5)

        sprite.setScale(scale)
        sprite.size = scene!.size
        parent.addChild(sprite)

        sprite.position = CGPoint.zero
        return sprite
    }

    func drawMarkerSprite(parent: SKNode, scale: CGFloat) -> SKSpriteNode {
        let sprite = SKSpriteNode(texture: spriteTexture)

        sprite.size = CGSize(width: bottomSprite.size.width * 0.05, height: bottomSprite.size.height * 0.05)
        sprite.colorBlendFactor = 1.0
        sprite.anchorPoint = CGPoint(x: 0.5, y: 0.5)

        let x = Int.random(in: Int(-self.bottomSprite.size.width)..<Int(self.bottomSprite.size.width))
        let y = Int.random(in: Int(-self.bottomSprite.size.height)..<Int(self.bottomSprite.size.height))

        sprite.position = CGPoint(x: x, y: y)
        parent.addChild(sprite)

        return sprite
    }

    override func update(_ currentTime: TimeInterval) {
        if !setupComplete {
            bottomSprite = drawContainerSprite(parent: self.scene!, scale: 0.5, color: .blue)
            middleSprite = drawContainerSprite(parent: bottomSprite, scale: 0.5, color: .orange)
            topSprite = drawContainerSprite(parent: middleSprite, scale: 1.0, color: .red)

            setupComplete = true
        }

        let markerSprite = drawMarkerSprite(parent: self.scene!, scale: 1.0)
        markerSprite.color = getMarkerColor(outerSprite: topSprite, innerSprite: markerSprite)
    }
}
SaganRitual
  • 3,143
  • 2
  • 24
  • 40

1 Answers1

2

Here is what you're missing: SKNode.contains(_:) is not the same as CGRect.contains(_:).

SKNode.contains(_:) is not about rectangles. It's about hit-testing whether a point is inside a parent node. CGRect.contains(_:) is about rectangles, and it knows nothing about nodes, or scaling, or SpriteKit. If you're going to use a sprite's frame to check whether the gremlins are getting out of jail, you'll have to scale it yourself.

SaganRitual
  • 3,143
  • 2
  • 24
  • 40