ARKit 动画 SCNNode
ARKit Animation SCNNode
我有一个 SCNNode,我在其中显示一个表面 - 我想在这个表面上显示一条路径。这个路径是一个 SCNNode 本身,它被添加到表面 SCNNode。这个 SCNNode(路径)由多个 SCNNode 组成,它们是整个路径的一小部分 - 所以我将它们全部添加到路径 SCNNode。
所以工作流程如下:
- 计算路径的 SCNNode 块
- 将块添加到完整路径SCNNode
- 添加每个SCNNode块时->添加表面SCNNode的完整路径
问题:我不只是想添加这个我想从头到尾(从第一个块到最后一个块)对其进行动画处理,但我该怎么做?
感谢您的帮助!
由于您没有提供任何代码(请下次提供),我将提供一个解决方案,为您指明正确的方向。
让我们从创建一个 PathItem Class
开始,我们将使用它来制作完整的路径,例如其中一行:
/// Path Item Node
class PathItem: SCNNode{
/// Creates A PathItem
///
/// - Parameters:
/// - size: CGFloat (Defaults To 20cm)
/// - texture: UIColour
/// - position: SCNVector3
init(size: CGFloat = 0.2, texture: UIColor, position: SCNVector3){
super.init()
//1. Create Our Path Geometry
let pathGeometry = SCNPlane(width: size, height: size)
//2. Assign The Colour To The Geoemtry
pathGeometry.firstMaterial?.diffuse.contents = texture
//3. Assign The Geometry, Position The Node & Rotate The Node So It Is Horizontal
self.geometry = pathGeometry
self.position = position
self.eulerAngles.x = GLKMathDegreesToRadians(-90)
}
required init?(coder aDecoder: NSCoder) { fatalError("Path Item Coder Has Not Been Implemented") }
}
完成此操作后,让我们创建一个 func
,这将创建一行 PathItem
(路径)。
首先像这样创建一个全局变量,它引用每个 PathItem
:
的大小
let pathItemSize: CGFloat = 0.2
然后我们创建我们的函数来交替每个 PathItem
的颜色,并为它们提供一个唯一的 name
或我们稍后将在动画中使用的索引:
/// Create A Path With A Number Of Elements
///
/// - Parameter numberOfElements: Int
/// - Returns: PATH (SCNNode)
func createdPathOfSize(_ numberOfElements: Int) {
var pathColour: UIColor!
//2. Loop Through The Number Of Path Elements We Want & Place Them In A Line
for pathIndex in 0 ..< numberOfElements{
//a. Position Each Peice Next To Each Other Based On The Index
let pathPosition = SCNVector3(0, -0.2, -pathItemSize * CGFloat(pathIndex+1))
//b. Alternate The Colour Of Our Path
if pathIndex % 2 == 0 { pathColour = UIColor.white } else { pathColour = UIColor.black }
//c. Create Our Path Item With A Unique Index We Can Use For Animating
let pathItem = PathItem(texture: pathColour, position: pathPosition)
pathItem.name = String(pathIndex)
//d. Set It To Hidden Initially
pathItem.isHidden = true
//e. Add It To Our Scene
self.augmentedRealityView.scene.rootNode.addChildNode(pathItem)
}
}
要生成 Path
我们现在可以这样做:
override func viewDidLoad() {
super.viewDidLoad()
//1. Set Up Our ARSession
augmentedRealityView.session = augmentedRealitySession
sessionConfiguration.planeDetection = .horizontal
augmentedRealityView.debugOptions = .showFeaturePoints
augmentedRealitySession.run(sessionConfiguration, options: [.resetTracking, .removeExistingAnchors])
//2. Create A Path Of 10 PathItems
createdPathOfSize(10)
}
因此我有以下 Global variables
:
@IBOutlet var augmentedRealityView: ARSCNView!
let augmentedRealitySession = ARSession()
let sessionConfiguration = ARWorldTrackingConfiguration()
现在我们已经生成了我们需要动画的路径!
为了提供一点多样性,让我们创建一个 Enum
,我们可以用它来创建不同的路径动画:
/// Path Item Animation
///
/// - UnHide: UnHides The Path Item
/// - FadeIn: Fades The Path Item In
/// - FlipIn: Flips The Path Item In
enum AnimationType{
case UnHide
case FadeIn
case FlipIn
}
不,因为我们要制作一些动画,让我们再创建 2 个全局变量,这些变量将用于 运行 我们在定时器上的动画并跟踪我们的进度:
var pathAnimationTimer: Timer?
var time: Int = 0
现在让我们创建动画函数:
/// Animates The Laying Of The Path
///
/// - Parameters:
/// - numberOfElements: Int
/// - animation: AnimationType
func animatePathElements(_ numberOfElements: Int, withAnimation animation: AnimationType ){
//1. If We Are Flipping The PathItems In We Need To 1st Unhide Them All & Rotate Them To A Vertical Postions
if animation == .FlipIn {
let pathItems = self.augmentedRealityView.scene.rootNode.childNodes
pathItems.forEach({ (pathItemToAnimate) in
pathItemToAnimate.isHidden = false
pathItemToAnimate.eulerAngles.x = GLKMathDegreesToRadians(0)
})
}
//2. Create Our Time Which Will Run Every .25 Seconds
pathAnimationTimer = Timer.scheduledTimer(withTimeInterval: 0.25, repeats: true) { timer in
//3. Whilst Our Time Doesnt Equal The Number Of Path Items Then Continue Our Animation
if self.time != numberOfElements{
//a. Get The Current Node Remembering Each One Has A Unique Name (Index
guard let pathItemToAnimate = self.augmentedRealityView.scene.rootNode.childNode(withName: "\(self.time)", recursively: false) else { return }
//b. Run The Desired Animation Sequence
switch animation{
case .UnHide:
//Simply Unhide Each PathItem
pathItemToAnimate.isHidden = false
case .FadeIn:
//1. Unhide The Item & Sets It's Opacity To 0 Rendering It Invisible
pathItemToAnimate.isHidden = false
pathItemToAnimate.opacity = 0
//2. Create An SCNAction To Fade In Our PathItem
let fadeInAction = SCNAction.fadeOpacity(to: 1, duration: 0.25)
pathItemToAnimate.runAction(fadeInAction)
case .FlipIn:
//Simply Rotate The Path Item Horizontally
pathItemToAnimate.eulerAngles.x = GLKMathDegreesToRadians(-90)
}
self.time += 1
}else{
//4. Our Animation Has Finished So Invalidate The Timer
self.pathAnimationTimer?.invalidate()
self.time = 0
}
}
}
然后我们需要将其添加到我们的 createPathOfSize 函数的末尾,如下所示:
/// Create A Path With A Number Of Elements Which Can Be Animated
///
/// - Parameter numberOfElements: Int
/// - Returns: PATH (SCNNode)
func createdPathOfSize(_ numberOfElements: Int) {
var pathColour: UIColor!
//1. Loop Through The Number Of Path Elements We Want & Place Them In A Line
for pathIndex in 0 ..< numberOfElements{
//a. Position Each Peice Next To Each Other Based On The Index
let pathPosition = SCNVector3(0, -0.2, -pathItemSize * CGFloat(pathIndex+1))
//b. Alternate The Colour Of Our Path
if pathIndex % 2 == 0 { pathColour = UIColor.white } else { pathColour = UIColor.black }
//b. Create Our Path Item With A Unique Index We Can Use For Animating
let pathItem = PathItem(texture: pathColour, position: pathPosition)
pathItem.name = String(pathIndex)
//c. Set It To Hidden Initially
pathItem.isHidden = true
//d. Add It To Our Scene
self.augmentedRealityView.scene.rootNode.addChildNode(pathItem)
}
//2. Animate The Path
animatePathElements(10, withAnimation: .FlipIn)
}
这里是完整的例子:
import UIKit
import ARKit
//----------------------
//MARK: - Path Animation
//----------------------
/// Path Item Animation
///
/// - Show: UnHides The Path Item
/// - FadeIn: Fades The Path Item In
enum AnimationType{
case UnHide
case FadeIn
case FlipIn
}
//-----------------
//MARK: - Path Item
//-----------------
/// Path Item Node
class PathItem: SCNNode{
/// Creates A PathItem
///
/// - Parameters:
/// - size: CGFloat (Defaults To 20cm)
/// - texture: UIColour
/// - position: SCNVector3
init(size: CGFloat = 0.2, texture: UIColor, position: SCNVector3){
super.init()
//1. Create Our Path Geometry
let pathGeometry = SCNPlane(width: size, height: size)
//2. Assign The Colour To The Geoemtry
pathGeometry.firstMaterial?.diffuse.contents = texture
//3. Assign The Geometry, Position The Node & Rotate The Node So It Is Horizontal
self.geometry = pathGeometry
self.position = position
self.eulerAngles.x = GLKMathDegreesToRadians(-90)
}
required init?(coder aDecoder: NSCoder) { fatalError("Path Item Coder Has Not Been Implemented") }
}
class ViewController: UIViewController {
typealias PATH = SCNNode
@IBOutlet var augmentedRealityView: ARSCNView!
let augmentedRealitySession = ARSession()
let sessionConfiguration = ARWorldTrackingConfiguration()
var pathPlaced = false
let pathItemSize: CGFloat = 0.2
var pathAnimationTimer: Timer?
var time: Int = 0
//----------------------
//MARK: - View LifeCycle
//----------------------
override func viewDidLoad() {
super.viewDidLoad()
//1. Set Up Our ARSession
augmentedRealityView.session = augmentedRealitySession
sessionConfiguration.planeDetection = .horizontal
augmentedRealityView.debugOptions = .showFeaturePoints
augmentedRealitySession.run(sessionConfiguration, options: [.resetTracking, .removeExistingAnchors])
//2. Create A Path Of 10 Path Items
createdPathOfSize(10)
}
//---------------------------------
//MARK: - Path Creation & Animation
//---------------------------------
/// Animates The Laying Of The Path
///
/// - Parameters:
/// - numberOfElements: Int
/// - animation: AnimationType
func animatePathElements(_ numberOfElements: Int, withAnimation animation: AnimationType ){
if animation == .FlipIn {
let pathItems = self.augmentedRealityView.scene.rootNode.childNodes
pathItems.forEach({ (pathItemToAnimate) in
pathItemToAnimate.isHidden = false
pathItemToAnimate.eulerAngles.x = GLKMathDegreesToRadians(0)
})
}
pathAnimationTimer = Timer.scheduledTimer(withTimeInterval: 0.25, repeats: true) { timer in
//1. Whilst Our Time Doesnt Equal The Number Of Path Items Then Continue Our Animation
if self.time != numberOfElements{
guard let pathItemToAnimate = self.augmentedRealityView.scene.rootNode.childNode(withName: "\(self.time)", recursively: false) else { return }
//2. Run The Desired Animation Sequence
switch animation{
case .UnHide:
pathItemToAnimate.isHidden = false
case .FadeIn:
pathItemToAnimate.isHidden = false
pathItemToAnimate.opacity = 0
let fadeInAction = SCNAction.fadeOpacity(to: 1, duration: 0.3)
pathItemToAnimate.runAction(fadeInAction)
case .FlipIn:
pathItemToAnimate.eulerAngles.x = GLKMathDegreesToRadians(-90)
}
self.time += 1
}else{
self.pathAnimationTimer?.invalidate()
self.time = 0
}
}
}
/// Create A Path With A Number Of Elements Which Can Be Animated
///
/// - Parameter numberOfElements: Int
/// - Returns: PATH (SCNNode)
func createdPathOfSize(_ numberOfElements: Int) {
var pathColour: UIColor!
//1. Loop Through The Number Of Path Elements We Want & Place Them In A Line
for pathIndex in 0 ..< numberOfElements{
//a. Position Each Peice Next To Each Other Based On The Index
let pathPosition = SCNVector3(0, -0.2, -pathItemSize * CGFloat(pathIndex+1))
//b. Alternate The Colour Of Our Path
if pathIndex % 2 == 0 { pathColour = UIColor.white } else { pathColour = UIColor.black }
//b. Create Our Path Item With A Unique Index We Can Use For Animating
let pathItem = PathItem(texture: pathColour, position: pathPosition)
pathItem.name = String(pathIndex)
//c. Set It To Hidden Initially
pathItem.isHidden = true
//d. Add It To Our Scene
self.augmentedRealityView.scene.rootNode.addChildNode(pathItem)
}
//2. Animate The Path
animatePathElements(10, withAnimation: .FlipIn)
}
}
这应该足以为您指明正确的方向^__________^.
我有一个 SCNNode,我在其中显示一个表面 - 我想在这个表面上显示一条路径。这个路径是一个 SCNNode 本身,它被添加到表面 SCNNode。这个 SCNNode(路径)由多个 SCNNode 组成,它们是整个路径的一小部分 - 所以我将它们全部添加到路径 SCNNode。
所以工作流程如下:
- 计算路径的 SCNNode 块
- 将块添加到完整路径SCNNode
- 添加每个SCNNode块时->添加表面SCNNode的完整路径
问题:我不只是想添加这个我想从头到尾(从第一个块到最后一个块)对其进行动画处理,但我该怎么做?
感谢您的帮助!
由于您没有提供任何代码(请下次提供),我将提供一个解决方案,为您指明正确的方向。
让我们从创建一个 PathItem Class
开始,我们将使用它来制作完整的路径,例如其中一行:
/// Path Item Node
class PathItem: SCNNode{
/// Creates A PathItem
///
/// - Parameters:
/// - size: CGFloat (Defaults To 20cm)
/// - texture: UIColour
/// - position: SCNVector3
init(size: CGFloat = 0.2, texture: UIColor, position: SCNVector3){
super.init()
//1. Create Our Path Geometry
let pathGeometry = SCNPlane(width: size, height: size)
//2. Assign The Colour To The Geoemtry
pathGeometry.firstMaterial?.diffuse.contents = texture
//3. Assign The Geometry, Position The Node & Rotate The Node So It Is Horizontal
self.geometry = pathGeometry
self.position = position
self.eulerAngles.x = GLKMathDegreesToRadians(-90)
}
required init?(coder aDecoder: NSCoder) { fatalError("Path Item Coder Has Not Been Implemented") }
}
完成此操作后,让我们创建一个 func
,这将创建一行 PathItem
(路径)。
首先像这样创建一个全局变量,它引用每个 PathItem
:
let pathItemSize: CGFloat = 0.2
然后我们创建我们的函数来交替每个 PathItem
的颜色,并为它们提供一个唯一的 name
或我们稍后将在动画中使用的索引:
/// Create A Path With A Number Of Elements
///
/// - Parameter numberOfElements: Int
/// - Returns: PATH (SCNNode)
func createdPathOfSize(_ numberOfElements: Int) {
var pathColour: UIColor!
//2. Loop Through The Number Of Path Elements We Want & Place Them In A Line
for pathIndex in 0 ..< numberOfElements{
//a. Position Each Peice Next To Each Other Based On The Index
let pathPosition = SCNVector3(0, -0.2, -pathItemSize * CGFloat(pathIndex+1))
//b. Alternate The Colour Of Our Path
if pathIndex % 2 == 0 { pathColour = UIColor.white } else { pathColour = UIColor.black }
//c. Create Our Path Item With A Unique Index We Can Use For Animating
let pathItem = PathItem(texture: pathColour, position: pathPosition)
pathItem.name = String(pathIndex)
//d. Set It To Hidden Initially
pathItem.isHidden = true
//e. Add It To Our Scene
self.augmentedRealityView.scene.rootNode.addChildNode(pathItem)
}
}
要生成 Path
我们现在可以这样做:
override func viewDidLoad() {
super.viewDidLoad()
//1. Set Up Our ARSession
augmentedRealityView.session = augmentedRealitySession
sessionConfiguration.planeDetection = .horizontal
augmentedRealityView.debugOptions = .showFeaturePoints
augmentedRealitySession.run(sessionConfiguration, options: [.resetTracking, .removeExistingAnchors])
//2. Create A Path Of 10 PathItems
createdPathOfSize(10)
}
因此我有以下 Global variables
:
@IBOutlet var augmentedRealityView: ARSCNView!
let augmentedRealitySession = ARSession()
let sessionConfiguration = ARWorldTrackingConfiguration()
现在我们已经生成了我们需要动画的路径!
为了提供一点多样性,让我们创建一个 Enum
,我们可以用它来创建不同的路径动画:
/// Path Item Animation
///
/// - UnHide: UnHides The Path Item
/// - FadeIn: Fades The Path Item In
/// - FlipIn: Flips The Path Item In
enum AnimationType{
case UnHide
case FadeIn
case FlipIn
}
不,因为我们要制作一些动画,让我们再创建 2 个全局变量,这些变量将用于 运行 我们在定时器上的动画并跟踪我们的进度:
var pathAnimationTimer: Timer?
var time: Int = 0
现在让我们创建动画函数:
/// Animates The Laying Of The Path
///
/// - Parameters:
/// - numberOfElements: Int
/// - animation: AnimationType
func animatePathElements(_ numberOfElements: Int, withAnimation animation: AnimationType ){
//1. If We Are Flipping The PathItems In We Need To 1st Unhide Them All & Rotate Them To A Vertical Postions
if animation == .FlipIn {
let pathItems = self.augmentedRealityView.scene.rootNode.childNodes
pathItems.forEach({ (pathItemToAnimate) in
pathItemToAnimate.isHidden = false
pathItemToAnimate.eulerAngles.x = GLKMathDegreesToRadians(0)
})
}
//2. Create Our Time Which Will Run Every .25 Seconds
pathAnimationTimer = Timer.scheduledTimer(withTimeInterval: 0.25, repeats: true) { timer in
//3. Whilst Our Time Doesnt Equal The Number Of Path Items Then Continue Our Animation
if self.time != numberOfElements{
//a. Get The Current Node Remembering Each One Has A Unique Name (Index
guard let pathItemToAnimate = self.augmentedRealityView.scene.rootNode.childNode(withName: "\(self.time)", recursively: false) else { return }
//b. Run The Desired Animation Sequence
switch animation{
case .UnHide:
//Simply Unhide Each PathItem
pathItemToAnimate.isHidden = false
case .FadeIn:
//1. Unhide The Item & Sets It's Opacity To 0 Rendering It Invisible
pathItemToAnimate.isHidden = false
pathItemToAnimate.opacity = 0
//2. Create An SCNAction To Fade In Our PathItem
let fadeInAction = SCNAction.fadeOpacity(to: 1, duration: 0.25)
pathItemToAnimate.runAction(fadeInAction)
case .FlipIn:
//Simply Rotate The Path Item Horizontally
pathItemToAnimate.eulerAngles.x = GLKMathDegreesToRadians(-90)
}
self.time += 1
}else{
//4. Our Animation Has Finished So Invalidate The Timer
self.pathAnimationTimer?.invalidate()
self.time = 0
}
}
}
然后我们需要将其添加到我们的 createPathOfSize 函数的末尾,如下所示:
/// Create A Path With A Number Of Elements Which Can Be Animated
///
/// - Parameter numberOfElements: Int
/// - Returns: PATH (SCNNode)
func createdPathOfSize(_ numberOfElements: Int) {
var pathColour: UIColor!
//1. Loop Through The Number Of Path Elements We Want & Place Them In A Line
for pathIndex in 0 ..< numberOfElements{
//a. Position Each Peice Next To Each Other Based On The Index
let pathPosition = SCNVector3(0, -0.2, -pathItemSize * CGFloat(pathIndex+1))
//b. Alternate The Colour Of Our Path
if pathIndex % 2 == 0 { pathColour = UIColor.white } else { pathColour = UIColor.black }
//b. Create Our Path Item With A Unique Index We Can Use For Animating
let pathItem = PathItem(texture: pathColour, position: pathPosition)
pathItem.name = String(pathIndex)
//c. Set It To Hidden Initially
pathItem.isHidden = true
//d. Add It To Our Scene
self.augmentedRealityView.scene.rootNode.addChildNode(pathItem)
}
//2. Animate The Path
animatePathElements(10, withAnimation: .FlipIn)
}
这里是完整的例子:
import UIKit
import ARKit
//----------------------
//MARK: - Path Animation
//----------------------
/// Path Item Animation
///
/// - Show: UnHides The Path Item
/// - FadeIn: Fades The Path Item In
enum AnimationType{
case UnHide
case FadeIn
case FlipIn
}
//-----------------
//MARK: - Path Item
//-----------------
/// Path Item Node
class PathItem: SCNNode{
/// Creates A PathItem
///
/// - Parameters:
/// - size: CGFloat (Defaults To 20cm)
/// - texture: UIColour
/// - position: SCNVector3
init(size: CGFloat = 0.2, texture: UIColor, position: SCNVector3){
super.init()
//1. Create Our Path Geometry
let pathGeometry = SCNPlane(width: size, height: size)
//2. Assign The Colour To The Geoemtry
pathGeometry.firstMaterial?.diffuse.contents = texture
//3. Assign The Geometry, Position The Node & Rotate The Node So It Is Horizontal
self.geometry = pathGeometry
self.position = position
self.eulerAngles.x = GLKMathDegreesToRadians(-90)
}
required init?(coder aDecoder: NSCoder) { fatalError("Path Item Coder Has Not Been Implemented") }
}
class ViewController: UIViewController {
typealias PATH = SCNNode
@IBOutlet var augmentedRealityView: ARSCNView!
let augmentedRealitySession = ARSession()
let sessionConfiguration = ARWorldTrackingConfiguration()
var pathPlaced = false
let pathItemSize: CGFloat = 0.2
var pathAnimationTimer: Timer?
var time: Int = 0
//----------------------
//MARK: - View LifeCycle
//----------------------
override func viewDidLoad() {
super.viewDidLoad()
//1. Set Up Our ARSession
augmentedRealityView.session = augmentedRealitySession
sessionConfiguration.planeDetection = .horizontal
augmentedRealityView.debugOptions = .showFeaturePoints
augmentedRealitySession.run(sessionConfiguration, options: [.resetTracking, .removeExistingAnchors])
//2. Create A Path Of 10 Path Items
createdPathOfSize(10)
}
//---------------------------------
//MARK: - Path Creation & Animation
//---------------------------------
/// Animates The Laying Of The Path
///
/// - Parameters:
/// - numberOfElements: Int
/// - animation: AnimationType
func animatePathElements(_ numberOfElements: Int, withAnimation animation: AnimationType ){
if animation == .FlipIn {
let pathItems = self.augmentedRealityView.scene.rootNode.childNodes
pathItems.forEach({ (pathItemToAnimate) in
pathItemToAnimate.isHidden = false
pathItemToAnimate.eulerAngles.x = GLKMathDegreesToRadians(0)
})
}
pathAnimationTimer = Timer.scheduledTimer(withTimeInterval: 0.25, repeats: true) { timer in
//1. Whilst Our Time Doesnt Equal The Number Of Path Items Then Continue Our Animation
if self.time != numberOfElements{
guard let pathItemToAnimate = self.augmentedRealityView.scene.rootNode.childNode(withName: "\(self.time)", recursively: false) else { return }
//2. Run The Desired Animation Sequence
switch animation{
case .UnHide:
pathItemToAnimate.isHidden = false
case .FadeIn:
pathItemToAnimate.isHidden = false
pathItemToAnimate.opacity = 0
let fadeInAction = SCNAction.fadeOpacity(to: 1, duration: 0.3)
pathItemToAnimate.runAction(fadeInAction)
case .FlipIn:
pathItemToAnimate.eulerAngles.x = GLKMathDegreesToRadians(-90)
}
self.time += 1
}else{
self.pathAnimationTimer?.invalidate()
self.time = 0
}
}
}
/// Create A Path With A Number Of Elements Which Can Be Animated
///
/// - Parameter numberOfElements: Int
/// - Returns: PATH (SCNNode)
func createdPathOfSize(_ numberOfElements: Int) {
var pathColour: UIColor!
//1. Loop Through The Number Of Path Elements We Want & Place Them In A Line
for pathIndex in 0 ..< numberOfElements{
//a. Position Each Peice Next To Each Other Based On The Index
let pathPosition = SCNVector3(0, -0.2, -pathItemSize * CGFloat(pathIndex+1))
//b. Alternate The Colour Of Our Path
if pathIndex % 2 == 0 { pathColour = UIColor.white } else { pathColour = UIColor.black }
//b. Create Our Path Item With A Unique Index We Can Use For Animating
let pathItem = PathItem(texture: pathColour, position: pathPosition)
pathItem.name = String(pathIndex)
//c. Set It To Hidden Initially
pathItem.isHidden = true
//d. Add It To Our Scene
self.augmentedRealityView.scene.rootNode.addChildNode(pathItem)
}
//2. Animate The Path
animatePathElements(10, withAnimation: .FlipIn)
}
}
这应该足以为您指明正确的方向^__________^.