【问题标题】:How to update SceneKit nodes to 'automatically' reflect underlying model?如何更新 SceneKit 节点以“自动”反映底层模型?
【发布时间】:2015-07-08 00:19:48
【问题描述】:

在更新模型时,定位、更新和/或添加或删除与底层模型相对应的 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 手动搜索节点层次结构。但是,这确实不能很好地扩展(帧速率差),并且感觉“错误”,好像可能有一种更容易/更惯用的方式来做到这一点。我一直在看ReactiveCocoa 之类的东西,因为它似乎解决了这类问题,但我不确定这是否过于复杂。

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

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

【问题讨论】:

    标签: swift cocoa-touch scenekit reactive-cocoa


    【解决方案1】:

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

    他们建议使用NSNotifications 来提醒您的控制器(在您的情况下可能是您的SCNScene)您的模型发生了变化。然后您的控制器开始更改其视图(或在您的情况下为SCNNodes)。这是一个例子:

    1. 在您的模型中发生了一些事情 - 创建了一个新的 Foo!因此,发布NSNotification

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

    (为了能够在这里传递Foo 的实例,Foo 需要是一个类,因为structs 不能添加到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 是盲目通信。因此,您的控制器和节点可以是您想要的任何东西,而无需更改模型。

    希望对您有所帮助。

    【讨论】:

      【解决方案2】:

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

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

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

      var nodes = [String: SCNNode]()
      
      ...
      
      nodes[current.uid] = aNewNode
      

      您还可以随时在场景中添加和删除节点,无需“销毁和重建”。如果我们保留我之前的示例,您可以简单地这样做:

      nodes[uidToDelete].removeFromParentNode()
      

      附带说明,较差的帧速率可能与您添加/删除节点的方式无关。试试enable statistics on your scene,看看你在哪里浪费时间。

      【讨论】:

      • 谢谢。我会试试看。理想情况下,如果可能的话,我希望将表示与模型完全分开;它会让我的生活更轻松。如果我在 Foo 类中引用了 SceneKit,那么它的可移植性就会大大降低。帧率命中肯定来自我的childNodeWithName 调用——在某些情况下,对模型的更改会导致 SceneKit 表示中的级联更新,它会导致搜索数百个可能实际存在或可能不存在的节点,这就是为什么它看起来是一个糟糕的方法。
      • 在这种情况下,只需将节点存储在字典中就可以了。
      猜你喜欢
      • 2014-08-13
      • 1970-01-01
      • 2014-04-26
      • 2023-04-10
      • 1970-01-01
      • 2021-08-17
      • 1970-01-01
      • 1970-01-01
      • 2021-03-23
      相关资源
      最近更新 更多