I'm testing ARKit implementation, and a part of my experiments is just to import an animated model and let it follow the AR Scene's movement like other static models included in Apple's sample projects.
I grabbed an animated 3D model from Apple's SceneKit example https://developer.apple.com/library/content/samplecode/SceneKitAnimations/Introduction/Intro.html and put it into the simple ARKit test project. https://viewar.com/apple-augmented-reality-arkit-for-ios/
The result is: it shows the model, and is animated as well but the model keeps "following" me instead of staying in the same position in the AR space. What's strange is it's not just sticking on the same position in the screen but it keeps floating above my head in the AR space.
I'm too new to 3D things to figure out what's going on. Do you guys have any idea?
The code is like this.
class ViewController: UIViewController, ARSCNViewDelegate {
@IBOutlet var sceneView: ARSCNView!
var _animations = [CAAnimation]()
var charNode : SCNNode!
override func viewDidLoad() {
super.viewDidLoad()
// Set the view's delegate
sceneView.delegate = self
// Show statistics such as fps and timing information
sceneView.showsStatistics = true
// Create a new scene (Keep this ship just as a reference)
let scene = SCNScene(named: "art.scnassets/ship.scn")!
// Set the scene to the view
sceneView.scene = scene
// Load the DAE file and the associated animations
loadSceneAndAnimations();
// Be idle by default
playAnimation(ASCAnimation.Walk);
}
// MARK: Playing animations
func playAnimation(_ animation: ASCAnimation) {
// Use the same animation key for all the animations except "idle".
// When we will add an animation it will replace the animation currently
// playing (if any) but the idle animation will remain active for ever.
let key:String = animation == .Idle ? "idleAnimation" : "otherAnimation";
// Add the animation - it will start playing right away
sceneView.scene.rootNode.addAnimation(_animations[animation.rawValue], forKey: key);
}
// MARK: Animation loading
func loadSceneAndAnimations () {
// Load the character from one of our dae documents, for instance "idle.dae"
let idleURL = Bundle.main.url(forResource: "art.scnassets/idle", withExtension: "dae");
let idleScene = try! SCNScene(url: idleURL!, options: nil);
// Merge the loaded scene into our main scene in order to
// place the character in our own scene
for child in idleScene.rootNode.childNodes {
sceneView.scene.rootNode.addChildNode(child)
}
// Load all the animations from their respective dae document
// The animation identifier can be found in the Node Properties inspector of the Scene Kit editor integrated into Xcode
loadAnimation(animation: .Attack, sceneName: "art.scnassets/attack", animationIdentifier: "attackID");
loadAnimation(animation: .Die, sceneName: "art.scnassets/die", animationIdentifier: "DeathID");
loadAnimation(animation: .Idle, sceneName: "art.scnassets/idle", animationIdentifier: "idleAnimationID");
loadAnimation(animation: .Run, sceneName: "art.scnassets/run", animationIdentifier: "RunID");
loadAnimation(animation: .Walk, sceneName: "art.scnassets/walk", animationIdentifier: "WalkID");
}
func loadAnimation(animation:ASCAnimation, sceneName:String, animationIdentifier:String) {
let sceneURL = Bundle.main.url(forResource: sceneName, withExtension: "dae");
let sceneSource = SCNSceneSource(url: sceneURL!, options: nil);
let animationObject = sceneSource?.entryWithIdentifier(animationIdentifier, withClass: CAAnimation.self)
// Store the animation for later use
_animations.append(animationObject!);
// Whether or not the animation should loop
if animation == .Idle || animation == .Run || animation == .Walk {
animationObject?.repeatCount = MAXFLOAT;
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// Create a session configuration
let configuration = ARWorldTrackingSessionConfiguration()
// Run the view's session
sceneView.session.run(configuration)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
// Pause the view's session
sceneView.session.pause()
}
func session(_ session: ARSession, didFailWithError error: Error) {
// Present an error message to the user
}
func sessionWasInterrupted(_ session: ARSession) {
// Inform the user that the session has been interrupted, for example, by presenting an overlay
}
func sessionInterruptionEnded(_ session: ARSession) {
// Reset tracking and/or remove existing anchors if consistent tracking is required
}
}