Hyoryusha
Hyoryusha

Reputation: 207

Phantom Button in SpriteKit Scene

I have a simple game in which players get three rounds to achieve the highest score . The gameScene exists inside a SwiftUI View and is created like this:

var gameScene: SKScene {
       let scene = NyonindoGameScene(
           size: CGSize(
               width: UIScreen.main.bounds.width,
               height: UIScreen.main.bounds.height
           )
       )
       scene.viewModel = self.viewModel
       scene.scaleMode = .aspectFill
       return scene
       }

It is called from the body of the view (inside a GeometryReader inside a ZStack) using SpriteView(). The code was working great until I tested on a new iPhone 13, which gave me all kinds of quirky and unexpected behaviors. I won't elaborate on them now as I have fixed most, but I am still left with a "phantom" start button. It is designed to display different text depending on the round being played (viz.: "Start," "Try Again," "Last Chance") using a var that is accurately counting rounds. However, I get this at the end of the first round:

screenshot of button with overlapping test

When this Frankenstein button gets tapped, the new round begins. HOWEVER, SKPhysicsContactDelegate didBegin(_:) does not get called and collisions are ignored. (In my general bafflement here, I don't know if this is a separate issue or one that will go away when I solve the overlapping button problem.) In any case, here is the relevant code for the startButton:

func addStartButton(text: String) {
        startButton.removeFromParent() // added as one of many failed remedies
        let startButtonLabel = SKLabelNode(text: text)
        startButtonLabel.fontName = SKFont.bold
        startButtonLabel.fontSize = 40.0
        startButtonLabel.fontColor = UIColor.white
        startButtonLabel.position = CGPoint(x: 0, y: -12)
        startButton.position = CGPoint(x:self.frame.midX, y:self.frame.midY)
        startButton.zPosition = 3
        startButton.addChild(startButtonLabel)
        addChild(startButton)
    }

The initial start button is called like this in didMove(to view: SKView):

if attempts == 0 {
            addStartButton(text: "Start")
        }

And the buttons for the second and third round are called inside a gameOver() function like this:

if attempts ==  1 {
  startButton.removeFromParent() // again, this is overkill as it gets removed before this...
  let text: String = "Try Again!"
  addStartButton(text: text)
            }
if attempts ==  2 {
  startButton.removeFromParent() 
  let text: String = "Last Chance!"
  addStartButton(text: text)
            }

I originally had a switch statement instead of the two if statements, but that generated the same problem. Print statements to the console suggest that only one button is being called for each round, but the results suggest something different. Any clues? (Apologies if I haven't provided enough code for an assessment.)

Upvotes: 0

Views: 85

Answers (1)

Simone Pistecchia
Simone Pistecchia

Reputation: 2832

why are you removing the button? change it's label:

enter image description here

class TTESTGameScene: SKScene {
    
    var allBoxes: [SKSpriteNode] = []
    
    var startButton: SKShapeNode = SKShapeNode(rect: CGRect(x: 0, y: 0, width: 200, height: 43), cornerRadius: 20)
    
    override func didMove(to view: SKView) {
        physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
        view.allowsTransparency = true
        self.backgroundColor = .clear
        view.alpha = 1.0
        view.isOpaque = true
        view.backgroundColor = SKColor.clear.withAlphaComponent(0.0)
        
        let nextButton = SKShapeNode(rect: CGRect(x: 0, y: view.frame.maxY - 40, width: 66, height: 33), cornerRadius: 20)
        nextButton.fillColor = .yellow
        nextButton.name = "nextButton"
        let nextLabel = SKLabelNode(text: "")
        nextLabel.fontSize = 40.0
        nextLabel.fontColor = UIColor.white
        nextLabel.position = CGPoint(x: 0, y: -12)
        nextButton.addChild(nextLabel)
        addChild(nextButton)
   
        
        startButton.fillColor = .red
        startButton.name = "startButton"

        let startButtonLabel = SKLabelNode(text: "000")
        startButtonLabel.fontSize = 30.0
        startButtonLabel.fontColor = UIColor.white
        startButtonLabel.horizontalAlignmentMode = .center
        
        startButtonLabel.position = CGPoint(x: startButton.frame.size.width/2, y: 10)
        startButtonLabel.name = "startButtonLabel"
        
        startButton.position = CGPoint(x:self.frame.midX - startButton.frame.size.width/2, y:self.frame.midY)
        startButton.zPosition = 3
        startButton.addChild(startButtonLabel)
        addChild(startButton)
                
    }

    var attempts: Int = 0
    
    func nextLevel() {
        //startButton.removeFromParent() // added as one of many failed remedies
        var text = ""
        if attempts == 0 {
            text = "Start"
        }
        else if attempts ==  1 {
            text = "Try Again!"
        }
        else if attempts ==  2 {
          text = "Last Chance!"
        }
        if let label = startButton.childNode(withName: "//startButtonLabel") as? SKLabelNode {
            label.text = text
            attempts += 1
            attempts = attempts > 2 ? 0:attempts
        }
    }
    
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        guard let touch = touches.first else { return }
        let location = touch.location(in: self.view)
        
        let sceneTouchPoint = self.convertPoint(fromView: location)
        let touchedNode = self.atPoint(sceneTouchPoint)
        print(touchedNode.name)
        if touchedNode.name == "nextButton" {
            nextLevel()
        }
    }
}

// A sample SwiftUI creating a GameScene and sizing it
// at 300x400 points
struct TTESTContentView: View {
    var scene: SKScene {
        let scene = TTESTGameScene()
        scene.size = CGSize(width: 300, height: 400)
        scene.scaleMode = .aspectFill
        return scene
    }

    var body: some View {
        SpriteView(scene: scene)
            .frame(width: 300, height: 400)
            //.ignoresSafeArea()
    }
}
struct ContentViewTest_Previews: PreviewProvider {
    static var previews: some View {
        TTESTContentView()
    }
}

Upvotes: 1

Related Questions