SceneKit Animation run automatically - ios

Hey I make a jumping man animation in blender and I imported it in scene kit.
It works fine but there is a problem as I want to make the man not jump until user touch but this character start jumping automatically when I run my code.
How can I control animation not jump automatically?
I have this code:
class GameViewController: UIViewController {
var messiArmtr = SCNNode()
var KickAnimation = CAAnimation()
var BoxingAnimation = CAAnimation()
override func viewDidLoad() {
super.viewDidLoad()
let scene = SCNScene(named: "art.scnassets/messi.dae")!
let cameraNode = SCNNode()
cameraNode.camera = SCNCamera()
scene.rootNode.addChildNode(cameraNode)
let messiamtr = scene.rootNode.childNodeWithName("amtr", recursively: true)!
KickAnimation = CAAnimation.animationWithSceneNamed("art.scnassets/move.dae")!
messiamtr.addAnimation(KickAnimation, forKey: "amtr")

it's not really automatic, you asked it to be played when you added it:
messiamtr.addAnimation(KickAnimation, forKey: "amtr")

Related

iOS ARKit: Large size object always appears to move with the change in the position of the device camera

I am creating an iOS ARKit app where I wanted to place a large object in Augmented Reality.
When I am trying to place the object at a particular position it always appears to be moving with the change in camera position and I am not able to view the object from all angles by changing the camera position.
But if I reduce it's scale value to 0.001 (Reducing the size of the object), I am able to view the object from all angles and the position of the placed object also does not change to that extent.
Bounding Box of the Object:-
Width = 3.66
Height = 1.83
Depth = 2.438
Model/Object Url:-
https://drive.google.com/open?id=1uDDlrTIu5iSRJ0cgp70WFo7Dz0hCUz9D
Source Code:-
import UIKit
import ARKit
import SceneKit
class ViewController: UIViewController {
#IBOutlet weak var sceneView: ARSCNView!
private let configuration = ARWorldTrackingConfiguration()
private var node: SCNNode!
//MARK: - Life cycle
override func viewDidLoad() {
super.viewDidLoad()
self.sceneView.showsStatistics = false
self.sceneView.debugOptions = [ARSCNDebugOptions.showFeaturePoints]
self.sceneView.automaticallyUpdatesLighting = false
self.sceneView.delegate = self
self.addTapGesture()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
configuration.planeDetection = .horizontal
self.sceneView.session.run(configuration)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.sceneView.session.pause()
}
//MARK: - Methods
func addObject(hitTestResult: ARHitTestResult) {
let scene = SCNScene(named: "art.scnassets/Cube.obj")!
let modelNode = scene.rootNode.childNodes.first
modelNode?.position = SCNVector3(hitTestResult.worldTransform.columns.3.x,
hitTestResult.worldTransform.columns.3.y,
hitTestResult.worldTransform.columns.3.z)
let scale = 1
modelNode?.scale = SCNVector3(scale, scale, scale)
self.node = modelNode
self.sceneView.scene.rootNode.addChildNode(modelNode!)
let lightNode = SCNNode()
lightNode.light = SCNLight()
lightNode.light?.type = .omni
lightNode.position = SCNVector3(x: 0, y: 10, z: 20)
self.sceneView.scene.rootNode.addChildNode(lightNode)
let ambientLightNode = SCNNode()
ambientLightNode.light = SCNLight()
ambientLightNode.light?.type = .ambient
ambientLightNode.light?.color = UIColor.darkGray
self.sceneView.scene.rootNode.addChildNode(ambientLightNode)
}
private func addTapGesture() {
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(didTap(_:)))
self.sceneView.addGestureRecognizer(tapGesture)
}
#objc func didTap(_ gesture: UIPanGestureRecognizer) {
let tapLocation = gesture.location(in: self.sceneView)
let results = self.sceneView.hitTest(tapLocation, types: .featurePoint)
guard let result = results.first else {
return
}
let translation = result.worldTransform.translation
guard let node = self.node else {
self.addObject(hitTestResult: result)
return
}
node.position = SCNVector3Make(translation.x, translation.y, translation.z)
self.sceneView.scene.rootNode.addChildNode(self.node)
}
}
extension float4x4 {
var translation: SIMD3<Float> {
let translation = self.columns.3
return SIMD3<Float>(translation.x, translation.y, translation.z)
}
}
GIF of the Problem:-
Video URL of the Problem:-
https://drive.google.com/open?id=1E4euZ0ArEtj2Ffto1pAOfVZocV08EYKN
Approaches Tried:-
Tried to place the object at the origin
modelNode?.position = SCNVector3(0, 0, 0)
Tried to place the object at some distance away from the device camera
modelNode?.position = SCNVector3(0, 0, -800)
Tried with the different combinations of worldTransform/localTransform columns
modelNode?.position = SCNVector3(hitTestResult.worldTransform.columns.3.x, hitTestResult.worldTransform.columns.3.y, hitTestResult.worldTransform.columns.3.z)
modelNode?.position = SCNVector3(hitTestResult.worldTransform.columns.2.x, hitTestResult.worldTransform.columns.2.y, hitTestResult.worldTransform.columns.2.z)
modelNode?.position = SCNVector3(hitTestResult.worldTransform.columns.1.x, hitTestResult.worldTransform.columns.1.y, hitTestResult.worldTransform.columns.1.z)
modelNode?.position = SCNVector3(hitTestResult.worldTransform.columns.1.x, hitTestResult.worldTransform.columns.2.y, hitTestResult.worldTransform.columns.3.z)
modelNode?.position = SCNVector3(hitTestResult.localTransform.columns.3.x, hitTestResult.localTransform.columns.3.y, hitTestResult.localTransform.columns.3.z)
But still of no luck. It still appears to be moving with the device camera and not stuck to a position where it has been placed.
Expected Result:-
Object should be of actual size (Scale should be of 1.0). Their should be no reduction in the scale value.
Once placed at a particular position it should not move with the movement of the device camera.
Object can be seen from all angles with the movement of the device camera without any change in object position.
Unlike stated in the accepted answer, the issue is probably not about the tracking quality or a bug in the model. It looks like the model is not correctly placed on top of the ground, probably due to a mispositioned pivot point, and some part of the model stays under ground. So when you move the camera, since the part under ground is not occluded by the floor, it looks like it is shifting. Have a look at this picture:
The pivot points of the models provided by Apple are positioned correctly so that when it is placed on top of a plane on the ground, its parts stay above ground.
If you correctly position the pivot point of the model, it should work correctly, independent of the model type.
I found out the root cause of the issue. The issue was related to the Model which I was using for AR. When, I replaced the model with the one provided in this link:- https://developer.apple.com/augmented-reality/quick-look/. I was not facing any issues. So, if anyone face such type of issues in future I would recommend to use any of the model provided by Apple to check if the issue persists with it or not.
I experienced the same issue.
Whenever I try to change anything in our Model of .usdz type (which is actually an Encrypted and compressed type) we cannot edit or change anything in it. If I edit or change a little position then it behaves same way as highlighted in the question.
To handle this issue, I just moved the old model .usdz to trash and and copied the original file (again) to XCode and then it worked.

SceneKit camera causes objects to Not Show Up

I want to be able to create a custom camera node in SceneKit and view my scene from it (instead of the default camera).
However, I've been encountering a very strange issue with SceneKit:
If I use SCNCamera, nothing shows up in my scene.
If I don't use SCNCamera, the objects in my scene render
correctly.
This is the code I am using (very simple code; from a tutorial):
import UIKit
import SceneKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let sceneView = SCNView()
sceneView.frame = self.view.frame
self.view.addSubview(sceneView)
let scene = SCNScene()
sceneView.autoenablesDefaultLighting = true
sceneView.allowsCameraControl = true
let cameraNode = SCNNode()
// If the below line of code is commented out (so no SCNCamera is added), everything shows up
cameraNode.camera = SCNCamera()
scene.rootNode.addChildNode(cameraNode)
let sphere = SCNSphere(radius: 5)
sphere.firstMaterial?.diffuse.contents = UIColor.red
let sphereNode = SCNNode(geometry: sphere)
cameraNode.addChildNode(sphereNode)
sceneView.backgroundColor = UIColor.green
sceneView.scene = scene
}
}
This seems pretty straightforward, yet I can't find any reason why this is happening on SO, etc.
Strangely, I also observe that if I try to access the camera node via sceneView.pointOfView, I get nil, even though sceneView.allowsCameraControl is set to true
Any help is appreciated!
The sphere is a child node of the camera, without any offset (its position is (0, 0, 0)) and so the camera is inside the sphere. And if the sphere's material isn't doubleSided then you won't see anything.

how to add physics to the SceneKit scene? [duplicate]

This question already has an answer here:
Applying simple Physics to a .scn object in SceneKit XCODE SWIFT
(1 answer)
Closed 6 years ago.
Hey I have a scene kit scene and my goal is just to get the guy/character on the scene to fall do to gravity and then hit the floor. Thats it. the "Guy" is simple a node .scn node. and the ground is a also a .scn scene as shown in the code below. Ive gotten as far as the skills I have to try and add simple physics to the scene. I know how to add physics perfectly in SPRITEKIT but the .scn nodes don't let me attach the same variable that would use give the character physics. There are no errors in this code But the character is not affect by gravity when ran Thanks
Code:
// Collision bit masks
let BitmaskCollision = 1 << 2
let BitmaskCollectable = 1 << 3
let BitmaskEnemy = 1 << 4
let BitmaskFriend = 1 << 5
let BitmaskOutofBounds = 1 << 6
class GameViewController: UIViewController, ADBannerViewDelegate, SKPhysicsContactDelegate, SKSceneDelegate, SCNSceneRendererDelegate, SCNPhysicsContactDelegate{
// The character
let character = Character()
// The Playing Fields
let FieldScene = SCNScene(named: "art.scnassets/TesingCampusField.dae")!
override func viewDidLoad() {
super.viewDidLoad()
let scnView = self.view as! SCNView
scnView.scene = FieldScene
scnView.playing = true
scnView.delegate = self
scnView.scene!.physicsWorld.contactDelegate = self
//-----Adding Gravity----------------------------------------------
scnView.scene!.physicsWorld.gravity = SCNVector3(x: 0, y: 0, z: -9.8)
//------Add-the-Character-to-Scene--------------------
scnView.scene!.rootNode.addChildNode(character.node)
}
End Of code for this Controller
The Character is simply being set up from a file called "Character"
class Character {
let node = SCNNode()
init() {
let GuyScene = SCNScene(named: "art.scnassets/Guy.scn")
let characterTopLevelNode: SCNNode = GuyScene!.rootNode.childNodeWithName("Guy", recursively: true)!
node.addChildNode(characterTopLevelNode)
characterTopLevelNode.physicsBody?.contactTestBitMask = BitmaskEnemy
characterTopLevelNode.physicsBody?.collisionBitMask = BitmaskCollision
characterTopLevelNode.physicsBody?.categoryBitMask = BitmaskCollectable
characterTopLevelNode.physicsBody?.affectedByGravity = true
characterTopLevelNode.physicsBody?.friction = 0
characterTopLevelNode.physicsBody?.restitution = 0
characterTopLevelNode.physicsBody?.angularDamping = 0
// Below is the Creation of the Physics body of the character
let (min, max) = node.boundingBox
let collisionCapsuleRadius = CGFloat(max.x - min.x) * 0.4
let collisionCapsuleHeight = CGFloat(max.y - min.y)
let characterCollisionNode = SCNNode()
characterCollisionNode.name = "collider"
// position light above the floor so you dont hit it and cause a contact
characterCollisionNode.position = SCNVector3(0.0, collisionCapsuleHeight * 0.51, 0.0)
characterCollisionNode.physicsBody = SCNPhysicsBody(type: .Dynamic, shape:SCNPhysicsShape(geometry: SCNCapsule(capRadius: collisionCapsuleRadius, height: collisionCapsuleHeight), options:nil))
// Adding the Pysics body of the character called "characterCollisionNode" to the actually character
node.addChildNode(characterCollisionNode)
}
}
This seems to be an earlier version of this question Applying simple Physics to a .scn object in SceneKit XCODE SWIFT
And the answer is probably similar, you don't appear to be adding the node as a child of the scene.

Adding a spark particle sprite inside a view controller

I created an .sks particle emitter based on the spark template.
My app is a normal app (not a game). When a user clicks a button, I have a new View controller that shows modally over fullscreen so that I can blur the background.
In this modal, I created a view and gave it a class of SCNView see image below:
How can I load the particle .sks file to do the animation on that viewController on the Particles view?
Update
How to load a SceneKit particle systems in view controller?
As mentioned by #mnuages, you can use .scnp file instead of .sks, which is a SceneKit Particle System.
So the steps are:
Create a SceneKit Particle System, I called it ConfettiSceneKitParticleSystem.scnp
Then in your art-board, select the view and select the class SCNView for it like in the printscreen of the question
In your UIViewController:
class SomeVC: UIViewController {
#IBOutlet weak var particles: SCNView!
override func viewDidLoad() {
super.viewDidLoad()
let scene = SCNScene()
let particlesNode = SCNNode()
let particleSystem = SCNParticleSystem(named: "ConfettiSceneKitParticleSystem", inDirectory: "")
particlesNode.addParticleSystem(particleSystem!)
scene.rootNode.addChildNode(particlesNode)
particles.scene = scene
}
}
Et Voila...you have you animation :)
.sks files are SpriteKit particle systems. You can also create SceneKit particle systems in Xcode, they are .scnp files.
A .scnp file is basically an archived SCNParticleSystem that you can load with NSKeyedUnarchiver and add to your scene using -addParticleSystem:withTransform:.
It might be easier to create a SpriteKit Particle File (which is what you did). You can add it to your main view in your UIViewController.
Add this somewhere:
extension SKView {
convenience init(withEmitter name: String) {
self.init()
self.frame = UIScreen.main.bounds
backgroundColor = .clear
let scene = SKScene(size: self.frame.size)
scene.backgroundColor = .clear
guard let emitter = SKEmitterNode(fileNamed: name + ".sks") else { return }
emitter.name = name
emitter.position = CGPoint(x: self.frame.size.width / 2, y: self.frame.size.height / 2)
scene.addChild(emitter)
presentScene(scene)
}
}
To use:
override func viewWillAppear(_ animated: Bool) {
view.addSubview(SKView(withEmitter: "ParticleFileName"))
}

How to switch SKScenes in SpriteKit with Swift properly?

I am developing a game with 3 different SKScenes(scene1, scene2, scene3). In GameViewController i initialize all of them like this:
class GameViewController: UIViewController {
var hero = Hero()
var skView: SKView!
var scene1: SKScene!
var scene2: SKScene!
var scene3: SKScene!
override func viewDidLoad() {
super.viewDidLoad()
// init scenes
scene1 = SKScene(size: view.bounds.size, hero: hero)
scene2 = SKScene(size: view.bounds.size, hero: hero)
scene3 = SKScene(size: view.bounds.size, hero: hero)
scene1.scaleMode = .AspectFill
scene2.scaleMode = .AspectFill
scene3.scaleMode = .AspectFill
// set view
skView = self.view as SKView
// present the first scene
skView.presentScene(scene1)
}
My idea is to present the first scene at first and present(swith to) the other scene later(i.e. when hero is stronger). In each scene the hero sprite was added like this:
func addHero() {
let heroSprite = SKSpriteNode(imageNamed: "hero")
hero.sprite = heroSprite
heroSprite.position = CGPoint(x: size.width/4, y: size.height/2)
addChild(heroSprite)
}
And in update method, hero position is updated by touching.
func update() {
if touching {
hero.position++
}
}
Hero class looks like this:
class Hero {
var sprite: SKSpriteNode?
}
The problem is:
The hero is movable by touching when only the first scene(scene1) is initialized. Which means, the hero is not movable any more with code above.
Can anyone give me some advice what have i done wrong? Thanks in advance!
PS: The complete codes can be found in Github.
The issue is solved by another related question: Sprites must be cleaned before switch scenes with SpriteKit and Swift?
By switching scenes, be sure not to add the sprites twice by checking the init status. For example in "didMoveToView", "init" or "setup" method:
if isInit {
do nothing
} else {
add sprites
isInit = true
}

Resources