Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Accessing a SpriteKit method from SwiftUI

I want to call a method defined inside a SpriteKit class with a SwiftUI button. I am using the @Observable macro on the class, and @State for the class instance inside SwiftUI.

Some of the class properties are accessible, but not the method I want to use. Calling it throws 3 compiler errors:

  • Cannot call value of non-function type ‘Binding’
  • Referencing subscript ‘subscript(dynamicMember:)’ requires wrapper ‘Binding’
  • Value of type ‘SKScene’ has no dynamic member ‘resetToInitialConditions’ using key path from root type ‘SKScene’

Using @Bindable instead of @State doesn’t solve the issue.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

What am I missing?

SwiftUI code:

import SwiftUI
import SpriteKit

struct ActionReplay: View {
    @State var myActionScene: SKScene = ActionScene(size: CGSize(width: 1000, height: 1000))
    
    var body: some View {
        ZStack {
            SpriteView(scene: myActionScene, debugOptions: [.showsFPS, .showsNodeCount])
                .ignoresSafeArea()
            VStack {
                Spacer()
                HStack {
                    Button(action: {
                        myActionScene.resetToInitialConditions() // Errors here
                    }, label: {
                        Text("Reset")
                    })
                    .buttonStyle(.bordered)
                }
            }
        }
    }
}

Data and SpriteKit:

struct InitialConditions {
    var position: CGPoint = .zero
    var rotation: CGFloat = 5
    var velocity: CGVector = CGVector(dx: 0, dy: 0)
    var angularVelocity: CGFloat = 0
}

@Observable class ActionScene: SKScene {
    
    var initialConditions = InitialConditions()
    var baseObject: SKSpriteNode!
    var ground: SKSpriteNode!
    
    override init(size: CGSize) {
        super.init(size: size)
        self.scaleMode = .resizeFill
        self.anchorPoint = CGPoint(x: 0.5, y: 0.5)
    }
    
    required init(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func didMove(to view: SKView) {
        backgroundColor = .white
        createObjects()
    }
    
    func createObjects() {
        baseObject = SKSpriteNode(color: .red, size: CGSize(width: 60, height: 60))
        baseObject.anchorPoint = CGPoint(x: 0.5, y: 0.5)
        baseObject.position = initialConditions.position
        baseObject.zRotation = initialConditions.rotation
        baseObject.physicsBody = SKPhysicsBody(rectangleOf: baseObject.size)
        baseObject.physicsBody?.restitution = 1.1
        baseObject.physicsBody?.linearDamping = 0
        addChild(baseObject)
        
        ground = SKSpriteNode(color: .darkGray, size: CGSize(width: 370, height: 10))
        ground.position = CGPoint(x: .zero, y: -300)
        ground.physicsBody = SKPhysicsBody(rectangleOf: ground.size)
        ground.physicsBody?.affectedByGravity = false
        ground.physicsBody?.isDynamic = false
        addChild(ground)
    }
    
    func resetToInitialConditions() {
        baseObject.physicsBody?.velocity = initialConditions.velocity
        baseObject.physicsBody?.angularVelocity = initialConditions.angularVelocity
        baseObject.position = initialConditions.position
        baseObject.zRotation = initialConditions.rotation
    }
    
    // Testing the method I want to call
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        resetToInitialConditions()
    }
}

>Solution :

You’ve defined your @State as:

@State var myActionScene: SKScene = ActionScene(size: CGSize(width: 1000, height: 1000))

This is telling the compiler that myActionScene is a SKScene. But, in reality, you have a subclass of SKScene, called ActionScene.

Because you’re telling the compiler that it’s really just a SKScene (which, technically it is — that is the superclass of ActionScene), it thinks that the only methods/properties available are those defined on SKScene. If you want access to what is defined on ActionScene, you must tell the compiler that’s what it is:

@State var myActionScene: ActionScene = ActionScene(size: CGSize(width: 1000, height: 1000))

Or, the compiler will even infer the type for you:

@State var myActionScene = ActionScene(size: CGSize(width: 1000, height: 1000))
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading