SpriteKit and SwiftUI. GameScene func not working from UI button

Viewed 208

I have a SKScene presented in a SwiftUI view. Trying to use SwiftUI buttons to add a Spritenode to the scene with a simple function.

If I run the function from the GameScene it works fine of course.

But if I try to access the function from the SwiftUI button nothing happens. The function runs (Print"image tapped" is in debug window) but the Sprite does not appear.

Why is this and how do I fix that?

My code

import SwiftUI
import SpriteKit

class GameScene: SKScene {
    override func didMove(to view: SKView) {
        
        
        physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
        //ball()
        
    }
    
    func ball() {
        
        let ball = SKShapeNode(circleOfRadius: 30)
        ball.position = CGPoint(x: 100, y: 100)
        ball.fillColor = .blue
        self.addChild(ball)
    }
}

struct ContentView: View {
    
    var scene: SKScene {
        let scene = GameScene()
        scene.size = CGSize(width: 800, height: 930)
        scene.scaleMode = .fill
        scene.backgroundColor = .red
        
        return scene
    }
    
    var body: some View {
        VStack {
            HStack {
                Button {
                    let makeBall = GameScene()
                    makeBall.ball()
                            print("Image tapped!")
                        } label: {
                            Image("playButton")
                                
                    }
                
                Button {
                            print("Image tapped!")
                        } label: {
                            Image("pauseButton")
                               
                    }
                
                
            }
            SpriteView(scene: scene)
                .frame(width: 800, height: 930, alignment: .center)
            
        }
        
        
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}
1 Answers

Two problems:

  1. Your scene is a computed property so you are recreating the scene every time you try use it in SpriteView.
  2. You are creating a new scene when you try add the ball. This should be added to the current scene, not a new instance.

Change scene to this:

let scene: GameScene = {
    let scene = GameScene()
    scene.size = CGSize(width: 800, height: 930)
    scene.scaleMode = .fill
    scene.backgroundColor = .red

    return scene
}()

And change the action on the pause button to this:

scene.ball()
print("Image tapped!")

Side note: with a fixed scene size like this, you got to be careful - you can only really see the play/pause buttons on iPad screens since the scene size is so big. Consider keeping it at a specific aspect ratio instead, then fitting it to the screen.

Related