How to add custom animations in ios scene kit swift - ios

I am making a game with scene kit and part of it rolls some dice to generate a number, i have made the dice in 3DS max 2016. I can make the animations easily on 3DS max, but how would i load it into my scene? I have loaded in a dice with no animations, but I am not sure how I can load the dice animations so that it rolls.
What I intend to do is make xcode generate a number, and with that number is chooses a file out of different files, e.g. animation1, animation2. Say the number generated was 2 it would run animation2.
Thanks

You can export animations from 3DS Max with OpenCOLLADA plugin.
Then you can get animations with SCNSceneSource, like:
let src = SCNSceneSource(URL: yourSceneURL, options: nil)
let node = src.entryWithIdentifier("yourNodeID", withClass: SCNNode.self) as SCNNode
let animation = node.entryWithIdentifier("yourAnimationID", withClass: CAAnimation.self) as CAAnimation
Extract animations to array, and attach random one to your node with SCNNode's addAnimation(forKey:)

Related

How can I access multiple animations in Swift?

Image of the file hierarchy in xcode of my animation file
Issue:
xcode is recognizing multiple animations for a single Collada (.dae) file. But I can't find any documentation on how to access these animations directly. I tried using the fox game example, but it only loads one of the animations.
Here's my code:
let modelNode = self.addModel_DAE_return(x: 0, y: 0, z: 0, scaleFactor: 0.0005, fileName: "models.scnassets/export_014/export_014_model.dae")
// add the animation to the model
let modelAnim = self.loadAnim_DAE(fileName: "models.scnassets/export_014/export_014_anim.dae")
modelNode.addAnimationPlayer(modelAnim, forKey: "headMove")
modelAnim.play()
// add the model to the scene
node.addChildNode(modelNode)
How can I access and load in the other animations?
Context:
I'm making an AR app. I'm using the Apple Image Recognition example as a base.
Here's a link to it:
https://developer.apple.com/documentation/arkit/recognizing_images_in_an_ar_experience
I animated a skeleton in Maya and exported the animation to a COLLADA (.DAE) file using the OpenCollada extension for Maya.
I exported separate files for the model and the animation, because if I export them as a single file, none of my animations export and xcode crashes every time I try to access the file to check if it registers any animations.
I want to access the "Entities" of my animation file so I can just loop over, load and attach the animations, but I can't
also I have looked into GameKit kind of but, is there not a way to do this with SceneKit?
Each joint in the rig has animation information
Originally I was only getting the information the first joint that had animations on it and not its children that also had animations attached
i.e. lets say your rig hierarchy is
hips > rightShoulder > arm > elbow > wrist
and your animations were on the shoulder, arm, elbow, and wrist joints
you do not have any animations on your hip joint
using enumerateChildNodes will only grab the information from the shoulder joint
using enumerateHierarchy will grab all the animation information from the shoulder, the elbow, AND the wrist joint
see below for my function that I used:
note that I had separately loaded and saved my 3D model onto an SCNNode and that was passed in so I could attach the animations to it
func loadAttachAnim_DAE(fileName: String, modelNode: SCNNode){
let scene = SCNScene(named: fileName)!
//find top level animation
var animationPlayer: SCNAnimationPlayer! = nil
scene.rootNode.enumerateHierarchy{ (child, stop) in
if !child.animationKeys.isEmpty {
print ("child = \(child) -----------------")
animationPlayer = child.animationPlayer(forKey: child.animationKeys[0])
modelNode.addAnimationPlayer(animationPlayer, forKey: "\(child)")
animationPlayer.play()
}
}
}

How can I load multiple nodes of the same 3d model all at once?

I'm working on an ARKit application where you would walk through a portal and you'd be somewhere else. I purchased a 3d model of an archway to serve as the portal. I took the model in to Blender to flatten it down to one piece, but the texture disappeared and whenever I apply a new texture it only shows up on one portion of the model.
Here is the original model, only modified by changing it to .dae format. however, I can't load arch1, arch2, arch3, etc. without writing the same line over and over
With this model I'd be writing the same code over and over
private func addPortalArch(hitResult: ARHitTestResult) {
let portalScene = SCNScene(named: "art.scnassets/Medieval_portal.dae")
let portalNode = portalScene?.rootNode.childNode(withName: "arch1", recursively: true)
// I'd be writing this portalNode = portalScene line over and over for each arch.
//I'd also have to get the position for each piece as far as I understand, which I do not know how I'd begin to do that based on where the user taps to place the portal.
portalNode?.position = SCNVector3(hitResult.worldTransform.columns.3.x, hitResult.worldTransform.columns.3.y, hitResult.worldTransform.columns.3.z)
self.sceneView.scene.rootNode.addChildNode(portalNode!)
}
I need the model to be all once piece but still have the same stonework material over the whole of it.
Looking at the file you sent, your best bet is to use the model which contains each aspect of the Arch (your 1st picture).
The 1st step (not always necessary but useful) is to convert the dae file to an .scn file:
Having done this you then want to create an Empty Node to be the Portal Door RootNode and make each element of the Portal a child of this e.g:
You will also need to rotate each ChildNode's (not the PortalRoot) Euler Angle X by -90 (as when you load the model the Portal Door is on it's side):
Now since you have a single RootNode, there is no need to loop through all the nodes so that is displayed on the screen. You can simple use a function like this:
/// Places The Portal Door 5m Aeay From The Camera
func setupPortalDoor(){
//1. Get The Portal Scene
guard let portalScene = SCNScene(named: "portalDoor.scn"),
//2. Get The Portal Node
let portalModel = portalScene.rootNode.childNode(withName: "PortalRoot", recursively: false) else { return }
//3. Add To The ARSCNView Root
augmentedRealityView?.scene.rootNode.addChildNode(portalModel)
//4. Position It 5m Away From The Camera
portalModel.position = SCNVector3(0, 0, -5)
/*
The Portal Is Large So I Suggest Scaling It ^__________*
*/
}
If you later wanted to change the portal arch to say marble you can easily do it like so:
for node in portalModel.childNodes{
node.geometry?.firstMaterial?.diffuse.contents = UIColor.cyan
}
Hope this helps...

SCNNode scale changes when animation plays using a dae file

Trying to use a dae file in scenekit for my model as well as my animations. When I try to scale the model, it scales correctly. After the animation starts playing, it resets to the original scale. Here is what I am trying to do at the moment:
let playerNode = gameScene.rootNode.childNode(withName: "Player", recursively: true)
let animation = CAAnimation.animationWithSceneNamed(name: "GameAssets.scnassets/Objects/WalkAnimation.dae")
playerNode.addAnimation(animation, forKey: "WalkAnimation")
As we don't have access to your Collada file, let's take the example of walk.dae from the SceneKitAnimations sample code.
In that file you will find the following:
<library_animations>
<animation id="WalkID">
...
<source id="node-Bip01_matrix-output">
...
<technique_common>
<accessor source="#node-Bip01_matrix-output-array" count="29" stride="16">
<param name="TRANSFORM" type="float4x4"/>
</accessor>
</technique_common>
You can see that the animation file does not have separate animations for positions and rotations, but instead it has a single animation that targets the whole transforms (cf TRANSFORM and float4x4).
This means that evaluating the animation will override the scale of the node. You'll have to have different animations just for the position and rotation properties instead of an animation for the transform property if you don't want the scale to be overridden.

Updating geometry in SpriteKit (or SceneKit)

We are porting a game to SpriteKit and I've run in to a bit of a problem. Some objects in our game have triangle-strip trails attached to them. The vertex buffers of the trails are continuously updated as the objects move in the world to create a seamless and flowing effect (there are constraints on how many vertices are in the buffer and how often we emit new vertices).
In the previous implementation we simply updated the affected vertices in the corresponding buffer whenever we emitted new vertices. In SceneKit it seems I am unable to update geometry sources, unless I use geometrySourceWithBuffer:vertexFormat:semantic:vertexCount:dataOffset:dataStride:. To do this however, it seems I need a Metal device, command queue and encoder, to be able to submit commands to update my buffer before it renders.
Is there any way I can do this with a normal SCNView, or do I have to do everything manually with a CAMetalLayer, creating my own metal device etc?
In essence, everything we need, bar this trail geometry is available in SpriteKit, so I was hoping there was some way I could get a hold of the metal device and command queue used by the SKView and simply use that to upload my geometry. This would make things a lot simpler.
I've used a particle system to get a similar effect. The particle emitter is attached to the moving object, with particles set to fade out and eventually die. You might need to set the emitter's targetNode to your scene, depending on the effect you want.
emitter = SKEmitterNode()
emitter.name = marbleNodeNames.trail.rawValue
emitter.particleTexture = SKTexture(imageNamed: "spark")
emitter.particleAlphaSpeed = -1.0
emitter.particleLifetime = 2
emitter.particleScale = 0.2
marbleSprite.addChild(emitter)

iOS Swift SpriteKit Achieving a sprite "explosion" effect

In my game I would like that when a collision occurs, the designated sprite would undergo an "explosion" or "glass break" effect, in which the sprite is split up into random pieces which are then moved at a random rate, speed, and angle. I would imagine that something like this may require using particles or at the very a least texture atlas.
I found a little bit on this, but the questions/explantations were catered for Objective-C. I am fairly new to iOS development and have solely used swift, so I can't really translate from one language to another. Thanks.
I would suggest you to try using the SpriteKit Emitter class for this. Add a new SpriteKit Particle Effect file to the project and configure the type of explosion there. You do not need any code, to configure it as Apple has very conveniently provided an editor window for us to easily change the values.
Once you are satisfied with the way the emitter looks, you can then open the Game Scene (assuming that is where this collision would be detected) and type:
let explosionEmitterNode = SKEmitterNode(fileNamed:"the file name")
sprite.addChild(explosionEmitterNode)
Here sprite is the actual node to which you would like to add the emitter effect to. Or you could add it to the scene directly and set its position as:
let explosionEmitterNode = SKEmitterNode(fileNamed:"the file name")
explosionEmitterNode.position = CGPointMake(200,300)
addChild(explosionEmitterNode)

Resources