Search code examples
iosxcodeswifttouchesbegan

select a point in the scene to place a node (SpriteKit, Swift)


I'd like to do so that when the player presses on a label, he then has to press once again on a point on the screen to pick a place where to build a house. Until now, here's what I've got: when the player presses on the label "build", a boolean ("wantsToBuild") is set to true, but for now obviously the house is built right on top of the label. I don't know either how to check if that place is already busy or if the player can build on there. I thought about having some placeholders but I wouldn't know how to set them up correctly. Could you help me solve this problem? thank you. EDIT: I've changed the code in the question following the suggestions in the answers but now i have a couple of problems: the joystick (which i didn't mention before) moves around even if i'm not touching the joystick itself. Plus even though I've checked with a print statement the value of wantsToBuild, and it seems set to false, whenever, i press on the screen, a house is alway built. Can you help me further?

class GameScene: SKScene {

var ship  = LCDShip()
var shipSpeed : CGFloat = 0.08
var base = SKSpriteNode(imageNamed: "base")
var joystick = SKSpriteNode(imageNamed: "joystick")
var joystickActive = false

var length:CGFloat! = nil
var xDist:CGFloat! = nil
var yDist:CGFloat! = nil

var deltaVector:CGVector! = nil

let build = SKLabelNode()
var wantsToBuild = false

override func didMoveToView(view: SKView) {

    build.name = "build"
    build.fontName = "Chalkduster"
    build.fontSize = 25
    build.text =  "Build a house"
    build.zPosition = 2
    build.position = CGPoint(x: self.frame.width/2, y: self.frame.height/2)
    addChild(build)


    backgroundColor = SKColor.blackColor()
    ship.position = CGPoint(x: self.frame.width/2, y: self.frame.height/2)
    addChild(ship)

    base.position = CGPoint(x: 150, y: 200)
    base.setScale(2)
    base.alpha = 0.3
    addChild(base)

    joystick.position = base.position
    joystick.setScale(2)
    joystick.alpha = 0.4
    joystick.name = "base"
    addChild(joystick)
}

override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {

    for touch in touches {
        let house = SKSpriteNode(imageNamed: "house")
        let location = touch.locationInNode(self)
        let node = nodeAtPoint(location)
        let label = self.childNodeWithName("build")!

        if node.name == "build"{
            print("where should i build the hosue?")
            wantsToBuild = true
        } else if wantsToBuild == true && node.name != "house" && location.x <= label.position.x - 15 || location.x >= label.position.x + 15 || location.y >= label.position.y + 15 || location.y <= label.position.y - 15 {
            house.position = location
            house.name = "house"
            addChild(house)
            wantsToBuild = false
        }
    }
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
    for touch in touches {
        let location = touch.locationInNode(self)
        deltaVector = CGVector(dx: location.x - base.position.x, dy: location.y - base.position.y)

        let angle = atan2(deltaVector.dy, deltaVector.dx)

        length = base.frame.size.height/2

        xDist = sin(angle - 1.57079633) * length
        yDist = cos(angle - 1.57079633) * length

        if(CGRectContainsPoint(base.frame, location)){
            joystick.position = location
        } else {
            joystick.position = CGPointMake(base.position.x - xDist, base.position.y + yDist)
        }

        ship.zRotation = angle - 1.57079633
    }
}

override func update(currentTime: NSTimeInterval) {
    if deltaVector != nil {
        ship.position.x += deltaVector.dx * shipSpeed
        ship.position.y += deltaVector.dy * shipSpeed
    }
}

}


Solution

  • Bogy's method will work, but you probably want it to be && node.name!= "build", unless you are building multiple houses, in which case you would probably want bothnode.name != "build" && node.name != "house"

    Anyway... This approach will work, unless your house sprite is big enough such then when you touch right next to the label, but not on the actual label, the house could overlap the label, and would basically be right on top of the label still. Again, IDK the sprite sizes and everything. To handle that scenario, you could create a range around the label that you are not allowed to place in, by doing this:

     let label = self.childNodeWithName("build")!
    
     if wantsToBuild == true && (location.x <= label.position.x - 15 || location.x >= label.position.x + 15 || location.y <= label.position.y - 15 || location.y >= label.position.y + 15) {
            let sprite = SKSpriteNode(imageNamed: "house")
            sprite.position = location
            addChild(sprite)
            wantsToBuild = false
        }
    

    This will kind of create a larger box around the label, and won't allow you to place the house in that box, so if you make the box big enough (you will probably have to tinker with the + 15 and - 15's), even if you put it as close as you can to the label, you can't place it close enough to overlap.

    If your label is a sprite, and not an SKLabelNode, you could do it how Bogy is saying, and just add a border of invisible space to the actual sprite image, so the closest point to the sprite that isn't in the sprite will be far enough away to not overlap.