如何将 SceneKit 节点更新为 'automatically' 反映底层模型?

How to update SceneKit nodes to 'automatically' reflect underlying model?

在更新模型时,定位、更新、and/or 添加或删除与底层模型对应的 SceneKit 节点的最明智方法是什么?

我不确定如何最好地表达这个问题,所以举一个简单的例子可能更容易:

我有一些东西,比方说彩色对象的集合,我可能想使用 Quartz 将二维表示为正方形,或者使用 SceneKit 进行三维表示。由于两种情况下的基础数据相同,因此抽象出模型并定义如下内容似乎更合适:

struct Foo {
    var uid: String
    var color: UIColor
    var position: [Float] // array of 3 floats for x, y, z
}

然后我有:

var collectionOfFoo: [Foo]

然后我可以通过遍历我的 collectionOfFoo 并为每个具有适当颜色和位置等的场景创建一个 SCNBox 来构建我的 SceneKit 场景

问题:用户可能会向 collectionOfFoo 添加新的 Foo,或删除现有的 Foo,或更改现有 Foo 的存储值,然后必须更新 SceneKit 场景以反映该值。销毁场景并从头开始重建它似乎很浪费,而且速度太慢了。这可能需要每秒调用数百次。

我目前的方法:我给对应于给定 Foo 的 SceneKit 节点一个与 Foouid 匹配的名称,然后最终手动搜索该节点使用 childNodeWithName 的层次结构。然而,这确实不能很好地扩展(帧速率差),而且感觉 "wrong" 好像可能有很多 easier/more 惯用的方法来做到这一点。我一直在研究 ReactiveCocoa 之类的东西,因为它似乎解决了这类问题,但我不确定这是否过于复杂。

如果我使用 Objective-C,我可能会尝试创建一个 Foo class,其中每个实例只包含一个指向相关 SceneKit 节点的指针;这不是很优雅,但至少会有效并且会给出预期的结果。我只是在寻找一种更好的方法,并且可以在 Swift.

中使用

非常欢迎提出建议,非常感谢 - 我仍在开发我的第一个应用程序,并且仍然认为自己是初学者。

我对 Swift 了解不多,所以大部分内容可能是错误的,但这里有一个大胆的猜测:

为什么不切换到 Class 而不是 Struct?您的代码不会改变太多,但它会帮助您存储 SCNNode 以及任何其他图形表示以及您拥有的数据。

如果这不可能,使用单独的词典怎么样?只需使用您已有的 uid 存储您的 SCNNodes:

var nodes = [String: SCNNode]()

...

nodes[current.uid] = aNewNode

您还可以随时在场景中添加和删除节点,无需 "destroy and rebuild"。如果我们保留我之前的例子,你可以简单地做:

nodes[uidToDelete].removeFromParentNode()

附带说明一下,低帧率可能与您 add/remove 节点的方式无关。尝试 enable statistics on your scene 看看你在哪里浪费了时间。

我建议你看看斯坦福大学的这门课程,看看 MVC 讲座:https://itunes.apple.com/gb/course/developing-ios-8-apps-swift/id961180099

他们建议使用 NSNotifications 来提醒您的控制器(在您的情况下可能是您的 SCNScene)模型的变化。然后你的控制器去改变它的视图(或 SCNNodes 在你的情况下)。这是一个例子:

1. 您的模型发生了一些变化 - 创建了一个新的 Foo!因此,post 一个 NSNotification:

let notificationCenter = NSNotificationCenter.defaultCenter()
notificationCenter.postNotificationName("FooWasCreatedNotification",
                                        object: nil,
                                        userInfo: ["Foo": fooInstance])

(为了能够在此处传递 Foo 的实例,Foo 需要是 class 因为 struct 无法添加到 userInfo)

2. 在您的 SCNScene(或您正在使用的任何其他内容)中,接收 NSNotification 并更新:

func didRevieveNewFooNotification(notification: NSNotification!) {
    if let info = notification.userInfo as? Dictionary<String,AnyObject>,
       let foo  = info["Foo"] as? Foo {

        //  Create new node using foo's properties...
        //  And add it to the array:
        collectionOfFoo.append(foo)
    }
}

使用 NSNotification 使您的模型非常便携,因为 NSNotification 是盲目的通信。因此,您的控制器和节点可以是您想要的任何东西,而无需更改模型。

希望对您有所帮助。