如何将 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 节点一个与 Foo
的 uid
匹配的名称,然后最终手动搜索该节点使用 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
他们建议使用 NSNotification
s 来提醒您的控制器(在您的情况下可能是您的 SCNScene
)模型的变化。然后你的控制器去改变它的视图(或 SCNNode
s 在你的情况下)。这是一个例子:
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
是盲目的通信。因此,您的控制器和节点可以是您想要的任何东西,而无需更改模型。
希望对您有所帮助。
在更新模型时,定位、更新、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 节点一个与 Foo
的 uid
匹配的名称,然后最终手动搜索该节点使用 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
他们建议使用 NSNotification
s 来提醒您的控制器(在您的情况下可能是您的 SCNScene
)模型的变化。然后你的控制器去改变它的视图(或 SCNNode
s 在你的情况下)。这是一个例子:
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
是盲目的通信。因此,您的控制器和节点可以是您想要的任何东西,而无需更改模型。
希望对您有所帮助。