Using SpriteKit inside SwiftUI

后端 未结 5 1403
说谎
说谎 2020-12-15 07:52

I am having an issue when creating a SpriteKit scene within SwiftUI. I created this project initially as a SwiftUI project.

Her

相关标签:
5条回答
  • 2020-12-15 08:06

    Here's a SpriteKit container View which can be used this way:

    SpriteKitContainer(sceneName: "MainScene")

    struct SpriteKitContainer : UIViewRepresentable {
    
        let sceneName: String
    
        class Coordinator: NSObject {
            var scene: SKScene?
        }
    
        func makeCoordinator() -> Coordinator {
            return Coordinator()
        }
    
        func makeUIView(context: Context) -> SKView {
            let view = SKView(frame: .zero)
            view.preferredFramesPerSecond = 60
            view.showsFPS = true
            view.showsNodeCount = true
    
           //load SpriteKit Scene
           guard let aScene = SKScene(fileNamed: sceneName)
           else {
                view.backgroundColor = UIColor.red
                return view
           }
           aScene.scaleMode = .resizeFill
           context.coordinator.scene = aScene
           return view
        }
    
    
        func updateUIView(_ view: SKView, context: Context) {
           view.presentScene(context.coordinator.scene)
        }
    
    }
    #if DEBUG
    struct ContentView_Previews : PreviewProvider {
    
       static var previews: some View {
    
          // Replace "MainScene" with your SpriteKit scene file name
          SpriteKitContainer(sceneName: "MainScene")
             .edgesIgnoringSafeArea(.all)
             .previewLayout(.sizeThatFits)
          }
    }
    #endif
    
    0 讨论(0)
  • 2020-12-15 08:16

    SwiftUI 2

    There is now a native view responsible for displaying a SKScene - it's called SpriteView.

    Assuming we have a simple SKScene:

    class Scene: SKScene {
        override func didMove(to view: SKView) {
            ...
        }
    }
    

    we can use a SpriteView to display it directly in a SwiftUI view:

    struct ContentView: View {
        var scene: SKScene {
            let scene = Scene()
            scene.size = CGSize(width: 300, height: 400)
            scene.scaleMode = .fill
            return scene
        }
    
        var body: some View {
            SpriteView(scene: scene)
                .frame(width: 300, height: 400)
                .edgesIgnoringSafeArea(.all)
        }
    }
    

    You can find more information here:

    • How to integrate SpriteKit using SpriteView.
    0 讨论(0)
  • 2020-12-15 08:16

    This is how i solved it:

    ContentView.swift

    import SwiftUI
    
    struct ContentView: View {
        var body: some View {
            SpriteKitContainer(scene: SpriteScene())
        }
    }
    
    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            ContentView()
        }
    }
    

    SpriteKitContainer.swift

    import SwiftUI
    import SpriteKit
    
    struct SpriteKitContainer: UIViewRepresentable {
        typealias UIViewType = SKView
        
        var skScene: SKScene!
        
        init(scene: SKScene) {
            skScene = scene
            self.skScene.scaleMode = .resizeFill
        }
        
        class Coordinator: NSObject {
            var scene: SKScene?
        }
        
        func makeCoordinator() -> Coordinator {
            let coordinator = Coordinator()
            coordinator.scene = self.skScene
            return coordinator
        }
        
        func makeUIView(context: Context) -> SKView {
            let view = SKView(frame: .zero)
            view.preferredFramesPerSecond = 60
            view.showsFPS = true
            view.showsNodeCount = true
            
            return view
        }
        
        func updateUIView(_ view: SKView, context: Context) {
            view.presentScene(context.coordinator.scene)
        }
    }
    
    struct SpriteKitContainer_Previews: PreviewProvider {
        static var previews: some View {
            Text("Hello, World!")
        }
    
    }
    

    SpriteKitScene.swift

    import UIKit
    import SpriteKit
    
    class SpriteScene: SKScene {
        
        //change the code below to whatever you want to happen on skscene
        
        override func didMove(to view: SKView) {
            physicsBody = SKPhysicsBody(edgeLoopFrom: frame)
        }
        
        override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
            guard let touch = touches.first else { return }
            
            let location = touch.location(in: self)
            let box = SKSpriteNode(color: .red, size: CGSize(width: 50, height: 50))
            box.position = location
            box.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 50, height: 50))
            addChild(box)
        }
    }
    

    PS: you'll only see the spritekitscene working in the simulator, it won't work in the preview

    0 讨论(0)
  • 2020-12-15 08:16

    A Coordinator is not necessary to present a scene; the Context is sufficient. Here is what I use to load a Scene.sks file:

    struct ContentView : View {
        var body: some View {
    
            SKViewContainer()
    
        }
    }
    
    struct SKViewContainer: UIViewRepresentable {
    
        func makeUIView(context: Context) -> SKView {
    
            let view = SKView() 
    
            guard let scene = SKScene(fileNamed: "Scene")
                else {
                    view.backgroundColor = UIColor.red
                    return view
            }
    
            view.presentScene(scene)
    
            return view    
        }
    
        func updateUIView(_ uiView: SKView, context: Context) {}
    
    }
    
    0 讨论(0)
  • 2020-12-15 08:18

    You SceneView implementation is incorrect.

    SwiftUI uses structs to builds views in its DSL - not views.

    You want to create a struct that conforms to UIViewRepresentable.

    struct SceneView: UIViewRepresentable {
    
        let scene: SKScene
    
        func makeUIView(context: Context) -> SKView {
            // Let SwiftUI handle the sizing
            return SKView(frame: .zero)
        }
    
        func updateUIView(_ uiView: SKView, context: Context) {
            uiView.presentScene(scene)
        }
    }
    

    For more info on how to port UIKit-based views to SwiftUI, see this excellent WWDC 2019 video: Integrating SwiftUI.

    0 讨论(0)
提交回复
热议问题