How to animate character on Demand SceneKit Xcode Swift - ios

hey I'm trying to figure out how to animate a .scn character i can get him in the scene but i can't lets say get him to play his animation when i press a button. Really i just need him to play his animation. back about a month ago i used to import the .scn file then the animation would play automatically as soon as he hit the screen but thats not what i need. I need it to play on demand and not just only when the app launches.
Code:
let GuyScene = SCNScene(named: "art.scnassets/Guy.scn")
let characterTopLevelNode: SCNNode = GuyScene!.rootNode.childNodeWithName("Guy", recursively: true)!
node.addChildNode(characterTopLevelNode)

Check these two Apple sample projects. They both have characters animated on demand:
Fox
Badger
The steps are:
Load a scn (or dae) file that contain the character and an animation (or just an animation and the skeleton of the character).
Trigger the animation with addAnimation:forKey: on the character's
root node.

I believe you should be able to use the paused property to achieve what you want.
Both SCNScene and SCNNode have this property. You should be able to pause the SCNNode and then unpause when you want the animation to run.
characterTopLevelNode.paused = true
// Or
GuyScene.paused = true
// Then to unpause just assign to false instead

Related

Pin Button on 3d Object in ARkit

I am trying to Pin button on a 3d object. When the TouchBegan function loads, I use touch to place object in the world. I want the buttons to be pinned on the object when i load the object.
I am unable to find AR Button in arkit. All i can found is to place text as child node and name the node and get the first hit result and get the child node by name to triggers buttons.
The Button placing to object with relative to 3d space is an issue. Looking for possible solutions.
This is what i wanted to achieve and currently am using the github project as a demo.
https://github.com/eh3rrera/ARKitSceneKitExample
I suggest using box/plane with SKScene as material for button and text in SKScene as title. Then you can use hitTest in tapGesture. And maybe animate color change to have button pressed effect.
The way I solved the button problem in ARKit wast to create a physical button as part of the 3D model
you don’t need to create buttons with text, for say tapping open the door or switching a light... but you need to somehow communicate to the user they can tap (the door to open or tap a light to switch on).
To active the node you give it a unique node.name “front_door”
node.name = “front_door”
One downside is you have to do naming of nodes using xcode before the program is executed.
Below is the swift code for responding to the tap on the button on the door... using a hitTest. The door would rotate open when the button was touched.
let tappedNode = self.sceneView.hitTest(gesture.location(in: gesture.view), options: [:])
if !tappedNode.isEmpty {
let tappedName = tappedNode[0].node.name
if tappedName == "front_door" {
let doorNode = self.sceneView.scene.rootNode.childNode(withName: tappedName, recursively: true)
// rotate the door open
doorNode?.eulerAngles = SCNVector3Make( 0,0,Float(2*M_PI))
}
}

SCNAnimationPlayer to start playing animation on gesture

I am using Scenekit/ ARKit for the code and Blender for my 3D models. I have a 3D model with some keyframe animations - created in Blender, which has been exported easily into XCode. Nothing complicated - it's just a cat moving a bit up and then down.
I am using animation controls inside my tapGestureRecognizer. As soon as my view loads I have set the animation to be in paused state :-
nonGeometryObjectNode?.childNode(withName: "Armature", recursively: true)?.animationPlayer(forKey: "cat_animated-1")?.paused = true
Code for animation controls inside my tap gesture is as below.
let animationPlayer = hitResults.first?.node.parent?.childNode(withName: "Armature", recursively: true)?.animationPlayer(forKey: "cat_animated-1")
animationPlayer?.animation.autoreverses = true
animationPlayer?.animation.repeatCount = 1
animationPlayer?.animation.duration = 0.8
animationPlayer?.animation.blendInDuration = 0.2
animationPlayer?.animation.blendOutDuration = 0.2
animationPlayer?.paused = true
if (animationPlayer?.paused)! {
animationPlayer?.play()
}
It works fine, the only problem is it works just one time when tap is performed for the very first time. I tried using .paused, .stop() inside my tapGesture code, but animationPlayer doesn't replay as it should every time I am tapping on it. There is no bool var in SCNAnimationPlayer or else which I can use to detect if the animation has played itself out, so that I can use .stop() and then .play() again.
I did see two instance vars animationDidStart and animationDidStop which I thought probably would be useful to manage what I need. But I am at loss of ways to use these. What would be really helpful is what should be used to play and stop/ pause my animation whenever I tap on the object. Any pointer would be helpful.
I know it's an old question but in case anyone stumbles across it in future, the default action is for the animation to be removed once complete. Therefore to be able to play it again you need to set:
animationPlayer?.animation.isRemovedOnCompletion = false

How to specify SCNAction to use scene time base

I'm trying to control playback of a scene by using SCNRenderer render(atTime:viewport:commandBuffer:passDescriptor:) method, according to Apple document:
By default, the playback timing of actions and animations in a scene
is based on the system time, not the scene time. Before using this
method to control the playback of animations, set the
uses​Scene​Time​Base property of each animation to YES, or specify the
SCNScene​Source​Animation​Import​Policy​Play​Using​Scene​Time​Base
option when loading a scene file that contains animations.
I can only specify the property uses​Scene​Time​Base of CoreAnimation animation. Is SCNAction have any property to change playback to scene time?

Send double tap to SCNView

SCNView build camera control has a function,double tap sceneview and roll back camera to start position when allowsCameraControl is enabled.
i went to add a button do the same thing
when user click the button i will roll back camera to start position
there is no class implement UIControl in scenekit
so i can't use sendAction
i can set camera position by scnView.pointOfView
but there is a animation when user scroll the camera, set camera position will fail when animation is running.
is there a good way to reset the camera ??
NicoS's answer effectively handles the part of your question about making a user action change the camera POV.
As for your issue with undesired animation when setting pointOfView — you can control that animation (and any other implicit animations that happen when you change object properties) using the SCNTransaction class. To make a change with no animation, just do this (Swift 3):
SCNTransaction.begin()
SCNTransaction.animationDuration = 0
// perform your changes...
view.pointOfView = newCameraNode
// ...and anything else you want to happen in the same non-animated update, then...
SCNTransaction.commit()
Just add a TapGestureRecognizer to your Storyboard, to the view that has the SCNView. Connect the gesture recognizer action to your class so that you have an IBAction. Set the number of taps to two taps in the Storyboard Attributes Inspector. Now you can add your code to reset the camera.

How to correctly pause the movements of sprites in a SpriteKit game?

I have a simple SpriteKit game, and in the appDelegate file in theapplicationWillResignActive(application: UIApplication)function, I put the code:GameScene().physicsWorld.speed = 0.0. However, when I close and reopen the app, a SKSpriteNode with a physicsBody attached carries on falling. I have also tried adding both these lines of code:
GameScene().speed = 0,
GameScene().paused = true. None of this works though. Any help would be appreciated.
You need to pause the SKView
self.scene?.view?.paused = true
Add an observer in your view controller, holding the view, for the willResignActive notification and then pause the SKView.
Add the observer for willEnterForeground to start it.

Resources