如何让 SKEmitterNode 在 SwiftUI 中工作? [英] How can I get SKEmitterNode to work in SwiftUI?
问题描述
我正在尝试更新 UIViewRepresentable
中 SKEmitterNode
的颜色.色调值是从父视图上的状态传入的,当父状态中的色调值更新时,发射器颜色应该更新.
I'm trying to update the colour of an SKEmitterNode
within a UIViewRepresentable
. The hue value is passed in from the state on the parent View, and the emitter colour should update when the hue value in the parent state updates.
它最初显示,尽管它在第一次调用 updateUIView
时更新,但它不会响应任何后续调用,即使该函数肯定会使用 hue<的新值被调用/code> 每次.
It initially displays, and although it updates on the first call to updateUIView
it does not respond to any subsequent calls, even though the function definitely gets called with the new value of hue
each time.
有谁知道为什么发射器不会更新?我正处于脱发阶段...
Has anyone any idea why the emitter won't update? I'm at the hair-tearing-out stage...
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)
}
}
推荐答案
现在 11 出来了,我终于可以玩 Swift UI 了.
Now that 11 is out I can finally play with Swift UI.
遇到的问题是您的 UIViewRepresentable
是一个结构体.
The problem being experienced is that your UIViewRepresentable
is a struct.
结构的工作方式是它们在写入时复制.
The way structs work is they are copy on write.
我将假设在您的场景委托中,您在设置色调时不会重新分配根控制器中的结构.
I am going to assume that in your scene delegate, you are not reassigning the struct in your root controller when you set the hue.
您需要将代码移到类中以获得最佳结果(不是协调器,它们用于应用程序流程.研究协调器模式以获取更多信息.)
You need to move your code into class for best results (Not the coordinator, they are used for application flow. Research Coordinator Pattern for more info.)
最好的课程是你的 SKScene.
The best class would be your SKScene.
我建议创建一个 GameScene 类并将您的 sks 文件更改为指向它.
I recommend making a GameScene class and changing your sks file to point to it.
然后您可以为此游戏场景类创建一个函数,该函数将允许您在不更改结构的情况下更改发射器.
You can then create a function to this game scene class that will allow you to alter the emitter without altering the struct.
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
将复制值.
The reason why this works is because on copy, the view
will copy the pointer, where as hue
will copy the value.
如果您需要在 updateUIView 中执行操作,您始终可以将 uiView.scene 转换为 GameScene 并以这种方式访问它.
If you need to do things in the updateUIView, you can always cast uiView.scene to GameScene and access it that way.
这篇关于如何让 SKEmitterNode 在 SwiftUI 中工作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!