如何让 SKEmitterNode 在 SwiftUI 中工作?
Posted
技术标签:
【中文标题】如何让 SKEmitterNode 在 SwiftUI 中工作?【英文标题】:How can I get SKEmitterNode to work in SwiftUI? 【发布时间】:2019-09-15 22:11:17 【问题描述】:我正在尝试更新 UIViewRepresentable
中 SKEmitterNode
的颜色。色调值是从父 View 上的状态传入的,当父状态的色调值更新时,发射器颜色也应该更新。
它最初显示,尽管它在第一次调用 updateUIView
时更新,但它不会响应任何后续调用,即使每次调用该函数时都会使用 hue
的新值。
有人知道为什么发射器不会更新吗?我正处于脱发阶段......
import SwiftUI
import UIKit
import SpriteKit
struct EmitterView: UIViewRepresentable
private let view = SKView()
let scene = SKScene(fileNamed: "myScene")!
var emitter: SKEmitterNode = SKEmitterNode()
var hue: Double
func makeUIView(context: UIViewRepresentableContext<EmitterView>) -> SKView
// Lets make it manually
emitter.particleTexture = SKTexture(imageNamed: "spark")
emitter.particleBirthRate = 80
emitter.particleLifetime = 2.5
emitter.particlePositionRange = CGVector(dx: 200, dy: 150)
emitter.particleScale = 0.2
emitter.particleScaleSpeed = 0.45
emitter.particleColor = SKColor.blue
emitter.particleColorBlendFactor = 1.0
scene.addChild(emitter)
view.presentScene(scene)
return view
func updateUIView(_ uiView: SKView, context: UIViewRepresentableContext<EmitterView>)
let color: SKColor = SKColor(
hue: CGFloat(hue),
saturation: 1.0,
brightness: 1.0,
alpha: 1.0
)
print("Hue is now", hue)
emitter.resetSimulation()
emitter.particleColor = color
struct EmitterView_Previews: PreviewProvider
static var previews: some View
EmitterView(hue: 0.5)
【问题讨论】:
【参考方案1】:现在 11 出来了,我终于可以玩 Swift UI了。
遇到的问题是您的UIViewRepresentable
是一个结构。
结构的工作方式是写入时复制。
我将假设在您的场景委托中,当您设置色调时,您不会重新分配根控制器中的结构。
您需要将代码移入类以获得最佳结果(不是协调器,它们用于应用程序流。研究协调器模式以获取更多信息。)
最好的课程是你的 SKScene。
我建议创建一个 GameScene 类并更改您的 sks 文件以指向它。
然后您可以为此游戏场景类创建一个函数,该函数将允许您在不更改结构的情况下更改发射器。
import SwiftUI
import UIKit
import SpriteKit
struct EmitterView: UIViewRepresentable
private let view = SKView()
let scene = GameScene(fileNamed: "myScene")!
var hue : Double
get
return scene.hue
set
scene.hue = newValue
func makeUIView(context: UIViewRepresentableContext<EmitterView>) -> SKView
view.presentScene(scene)
return view
func updateUIView(_ uiView: SKView, context: UIViewRepresentableContext<EmitterView>)
struct EmitterView_Previews: PreviewProvider
static var previews: some View
EmitterView()
class GameScene : SKScene
var hue : Double = 0
didSet
let color: SKColor = SKColor(
hue: CGFloat(hue),
saturation: 1.0,
brightness: 1.0,
alpha: 1.0
)
print("Hue is now", hue)
emitter.resetSimulation()
emitter.particleColor = color
var emitter: SKEmitterNode = SKEmitterNode()
override func didMove(to view: SKView)
emitter.particleTexture = SKTexture(imageNamed: "spark")
emitter.particleBirthRate = 80
emitter.particleLifetime = 2.5
emitter.particlePositionRange = CGVector(dx: 200, dy: 150)
emitter.particleScale = 0.2
emitter.particleScaleSpeed = 0.45
emitter.particleColor = SKColor.blue
emitter.particleColorBlendFactor = 1.0
addChild(emitter)
之所以可行,是因为在复制时,view
将复制指针,而 hue
将复制值。
如果您需要在 updateUIView 中进行操作,您可以随时将 uiView.scene 转换为 GameScene 并以这种方式访问它。
【讨论】:
感谢您抽出宝贵时间查看此内容。一切都说得通,而且是一个干净的解决方案。 @raffjones 没问题,我应该在看到 struct 后早点发现它,但它让我忘记了。一旦我开始使用它并让编译器立即对我大喊 contentView 不可变,它就引发了它失败的原因。【参考方案2】:为了使 UIViewRepresentable 结构可变,请将 @Binding 前缀添加到 hue 变量。
@Binding var hue: Double
然后您可以通过以下方式从父视图更改粒子的颜色:
struct ContentView: View
@State var hue: Double = 0.5
var body: some View
VStack
EmitterView(hue: self.$hue)
Slider(value: $hue)
【讨论】:
以上是关于如何让 SKEmitterNode 在 SwiftUI 中工作?的主要内容,如果未能解决你的问题,请参考以下文章
在 SpriteKit 中使用 SKEmitterNode 和粒子创建轨迹
SKEmitterNode 如何在不同背景下使用混合模式“添加”保持相同的效果