在 SwiftUI 中使用 SpriteKit
Using SpriteKit inside SwiftUI
我在 SwiftUI
中创建 SpriteKit
场景时遇到问题。我最初将此项目创建为 SwiftUI
项目。
这是我目前的代码:
ContentView.swift:
/// Where the UI content from SwiftUI originates from.
struct ContentView : View {
var body: some View {
// Scene
SceneView().edgesIgnoringSafeArea(.all)
}
}
SceneView.swift:
/// Creates an SKView to contain the GameScene. This conforms to UIViewRepresentable, and so can be used within SwiftUI.
final class SceneView : SKView, UIViewRepresentable {
// Conformance to UIViewRepresentable
func makeUIView(context: Context) -> SKView {
print("Make UIView")
return SceneView(frame: UIScreen.main.bounds)
}
func updateUIView(_ uiView: SKView, context: Context) {
print("Update UIView")
}
// Creating scene
override init(frame: CGRect) {
super.init(frame: frame)
let scene = Scene(size: UIScreen.main.bounds.size)
presentScene(scene)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Scene.swift:
/// The scene for the game in SpriteKit.
final class Scene : SKScene {
override func didMove(to view: SKView) {
super.didMove(to: view)
print("Scene didMove:")
}
}
问题
问题是场景多次重新加载,如日志所示(因为代码中有print
):
Scene didMove:
Make UIView
Scene didMove:
Update UIView
如您所见,Scene didMove:
被打印了两次。我只想调用一次,因为我想在这里创建我的精灵。有什么想法吗?
这是一个可以这样使用的 SpriteKit 容器视图:
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
呈现场景不需要协调器;上下文就足够了。这是我用来加载 Scene.sks 文件的内容:
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) {}
}
斯威夫特用户界面 2
现在有一个本地视图负责显示 SKScene
- 它叫做 SpriteView
。
假设我们有一个简单的 SKScene
:
class Scene: SKScene {
override func didMove(to view: SKView) {
...
}
}
我们可以使用 SpriteView
直接在 SwiftUI 视图中显示它:
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)
}
}
您可以在此处找到更多信息:
我是这样解决的:
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: 你只会看到 spritekitscene 在模拟器中工作,它不会在预览中工作
我在 SwiftUI
中创建 SpriteKit
场景时遇到问题。我最初将此项目创建为 SwiftUI
项目。
这是我目前的代码:
ContentView.swift:
/// Where the UI content from SwiftUI originates from.
struct ContentView : View {
var body: some View {
// Scene
SceneView().edgesIgnoringSafeArea(.all)
}
}
SceneView.swift:
/// Creates an SKView to contain the GameScene. This conforms to UIViewRepresentable, and so can be used within SwiftUI.
final class SceneView : SKView, UIViewRepresentable {
// Conformance to UIViewRepresentable
func makeUIView(context: Context) -> SKView {
print("Make UIView")
return SceneView(frame: UIScreen.main.bounds)
}
func updateUIView(_ uiView: SKView, context: Context) {
print("Update UIView")
}
// Creating scene
override init(frame: CGRect) {
super.init(frame: frame)
let scene = Scene(size: UIScreen.main.bounds.size)
presentScene(scene)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Scene.swift:
/// The scene for the game in SpriteKit.
final class Scene : SKScene {
override func didMove(to view: SKView) {
super.didMove(to: view)
print("Scene didMove:")
}
}
问题
问题是场景多次重新加载,如日志所示(因为代码中有print
):
Scene didMove:
Make UIView
Scene didMove:
Update UIView
如您所见,Scene didMove:
被打印了两次。我只想调用一次,因为我想在这里创建我的精灵。有什么想法吗?
这是一个可以这样使用的 SpriteKit 容器视图:
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
呈现场景不需要协调器;上下文就足够了。这是我用来加载 Scene.sks 文件的内容:
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) {}
}
斯威夫特用户界面 2
现在有一个本地视图负责显示 SKScene
- 它叫做 SpriteView
。
假设我们有一个简单的 SKScene
:
class Scene: SKScene {
override func didMove(to view: SKView) {
...
}
}
我们可以使用 SpriteView
直接在 SwiftUI 视图中显示它:
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)
}
}
您可以在此处找到更多信息:
我是这样解决的:
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: 你只会看到 spritekitscene 在模拟器中工作,它不会在预览中工作