swiftsprite-kitsknode

onTouchesBegan() not being called for my custom SKNode


I'm trying to design my own DPad for my game but for some reason my onTouchesBegan() is not being called. I explored a bit and I think the reason for this is because my self.frame.size is equal to (0,0) for some reason even after I have added my child nodes (up, down, left, right nodes). I'm guessing since the size is 0, then there's nothing to detect a touch on. Is there something I'm doing that's glaringly wrong here? The only thing I can think of is maybe because the buttons are SKLabelNodes? But I have printed their sizes and they are all > 0.

protocol DPadDelegate {
    func upTapped()
    func downTapped()
    func leftTapped()
    func rightTapped()
}

open class DPad: SKNode {

    private var buttonsSize: CGFloat

    private var up:    SKLabelNode!
    private var down:  SKLabelNode!
    private var left:  SKLabelNode!
    private var right: SKLabelNode!

    private var timer = Timer()

    private var upHeld = false
    private var downHeld = false
    private var leftHeld = false
    private var rightHeld = false

    var delegate: DPadDelegate?

    public lazy var width: CGFloat = {
        return buttonsSize * 3
    }()

    public lazy var height: CGFloat = {
        return buttonsSize * 3
    }()

    init(buttonsSize: CGFloat) {
        self.buttonsSize = buttonsSize

        super.init()

        let center = SKLabelNode(text: "⏹")
        up = SKLabelNode(text: "🔼")
        down = SKLabelNode(text: "🔽")
        left = SKLabelNode(text: "◀️")
        right = SKLabelNode(text: "▶️")

        center.fontSize = buttonsSize
        up.fontSize = buttonsSize
        down.fontSize = buttonsSize
        left.fontSize = buttonsSize
        right.fontSize = buttonsSize

        center.position = CGPoint(x: 0, y: 0)
        up.position = CGPoint(x: 0, y: buttonsSize)
        down.position = CGPoint(x: 0, y: -buttonsSize)
        left.position = CGPoint(x: -buttonsSize, y: 0)
        right.position = CGPoint(x: buttonsSize, y: 0)

        self.addChild(center)
        self.addChild(up)
        self.addChild(down)
        self.addChild(left)
        self.addChild(right)

        print(self.frame.size)
    }

    required public init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    open override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        print("?")

        if let touch = touches.first, up == atPoint(touch.location(in: self)) {
            upTapped()
            timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(upTapped), userInfo: nil, repeats: true)
        }
        if let touch = touches.first, down == atPoint(touch.location(in: self)) {
            downTapped()
            timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(downTapped), userInfo: nil, repeats: true)
        }
        if let touch = touches.first, left == atPoint(touch.location(in: self)) {
            leftTapped()
            timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(leftTapped), userInfo: nil, repeats: true)
        }
        if let touch = touches.first, right == atPoint(touch.location(in: self)) {
            rightTapped()
            timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(rightTapped), userInfo: nil, repeats: true)
        }
    }

    open override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {

        if let touch = touches.first {
            let location = touch.location(in: self)

            if upHeld && up != atPoint(location) {
                timer.invalidate()
                up.alpha = 1.0
                upHeld = false
            }
            if downHeld && down != atPoint(location) {
                timer.invalidate()
                down.alpha = 1.0
                downHeld = false
            }
            if rightHeld && right != atPoint(location) {
                timer.invalidate()
                right.alpha = 1.0
                rightHeld = false
            }
            if leftHeld && left != atPoint(location) {
                timer.invalidate()
                left.alpha = 1.0
                leftHeld = false
            }
        }
    }

    open override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        resetDPad()
    }

    open override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
        resetDPad()
    }

    private func resetDPad() {
        timer.invalidate()
        up.alpha = 1.0
        down.alpha = 1.0
        left.alpha = 1.0
        right.alpha = 1.0
        upHeld = false
        downHeld = false
        rightHeld = false
        leftHeld = false
    }

    @objc private func upTapped() {
        upHeld = true
        up.alpha = 0.6
        delegate?.upTapped()
    }

    @objc private func downTapped() {
        downHeld = true
        down.alpha = 0.6
        delegate?.downTapped()
    }

    @objc private func rightTapped() {
        rightHeld = true
        right.alpha = 0.6
        delegate?.rightTapped()
    }

    @objc private func leftTapped() {
        leftHeld = true
        left.alpha = 0.6
        delegate?.leftTapped()
    }

}

Solution

  • add isUserInteractionEnabled = true to your init func. Otherwise touches functions get ignored