Adding animation to 3D models in ARKit - ios

In this video an object is given an animation to hover around the room when placed and then when tapped it gets dropped down with another animation.
How can I add this kind of animations in my project?
Is adding an already animated object the only way?
Thank you
https://youtu.be/OS_kScr0XkQ

I think the hovering up and down is most likely an animation sequence. The following animation sequence for a hovering effect will work inside the first drop down selection function.
let moveDown = SCNAction.move(by: SCNVector3(0, -0.1, 0), duration: 1)
let moveUp = SCNAction.move(by: SCNVector3(0,0.1,0), duration: 1)
let waitAction = SCNAction.wait(duration: 0.25)
let hoverSequence = SCNAction.sequence([moveUp,waitAction,moveDown])
let loopSequence = SCNAction.repeatForever(hoverSequence)
node2Animate.runAction(loopSequence)
self.sceneView.scene.rootNode.addChildNode(node2Animate)
The second part to stop the animation when you tap the node put this inside the tap gesture function.
node2animate.removeAllActions()
The last part dropping to the floor, the node2animate might be a physicsBody and before the tap the gravity attribute
node2animate.physicsBody?.isAffectedByGravity = false
after the tap you set it to true
node2animate.physicsBody?.isAffectedByGravity = true
There is other stuff going on as well, collision with the floor have also been set etc.

Related

Animation to Remove Sprite

I am currently making a Sprite and I want it to animate before it disappears.
For example: I want it to animate it in the sense that it disappears from the top of the block until the bottom. To put it another way, I want to the size to decrease slowly until there is nothing left. But I want to give it the appearance that it is disappearing rather than scaling to nothing.
let hand = SKSpriteNode(imageNamed: "hand")
hand.size = CGSize(width: size.width/10, height: size.height/30)
hand.position = CGPoint(x: CGFloat(posX-2)*size.width/10+offsetX, y:CGFloat(posY)*size.height/30+offsetY)
addChild(hand)
tl;dr is it possible to make this sort of effect using SpriteKit in Swift.
Ideal Animation: https://ibb.co/sPsffmK
SKAction is an animation that is executed by a node in the scene. Actions are used to change a node in some way (like move its position over time), but you can also use actions to change the scene, like doing a fadeout.
In your case, you can apply multiple animations:
let move_action = SKAction.moveBy(x: -100, y: 0, duration: 1.0)
let scale_action = SKAction.scale(to: 0.0, duration: 1.0)
let fade_action = SKAction.fadeAlpha(to: 0.0, duration: 1.0)
hand.run(move_action)
hand.run(scale_action)
//hand.run(fade_action)
In the previous example, the hand runs the move and scale animation at the same time. But you can also make the hand to move to the position, and after it reaches, to scale it.
let sequence = SKAction.sequence([move_animation,scale_animation])
hand.run(sequence)
There are lots of animations that SKAction has, here you can find the complete list.

Swift Game Scene alter vertically moving background with time?

I have a moving background which is 1500 x 600 pixels and constantly moves vertically down the screen using this code:
let bgTexture = SKTexture(imageNamed: "bg.png")
let moveBGanimation = SKAction.move(by: CGVector(dx: 0, dy: -bgTexture.size().height), duration: 4)
let shiftBGAnimation = SKAction.move(by: CGVector(dx: 0, dy: bgTexture.size().height), duration: 0)
let moveBGForever = SKAction.repeatForever(SKAction.sequence([moveBGanimation, shiftBGAnimation]))
var i: CGFloat = 0
while i < 3 {
bg = SKSpriteNode(texture: bgTexture)
bg.position = CGPoint(x: self.frame.midX, y: bgTexture.size().height * i)
bg.size.width = self.frame.width
bg.zPosition = -2
bg.run(moveBGForever)
self.addChild(bg)
i += 1
}
I now want a new background to come onto the screen after x amount of time to give the feel the player is moving into a different part of the game.
Could I put this code into a function and trigger it with NSTimer after say 20 seconds but change the start position of the new bg to be off screen?
The trouble with repeatForever actions is you don't know where they are at a certain moment. NSTimers are not as precise as you'd like, so using a timer may miss the right time or jump in too early depending on rendering speeds and frame rate.
I would suggest replacing your moveBGForever with a bgAnimation as a sequence of your move & shift actions. Then, when you run bgAnimation action, you run it with a completion block of { self.cycleComplete = true }. cycleComplete would be a boolean variable that indicates whether the action sequence is done or not. In your scene update method you can check if this variable is true and if it is, you can run the sequence action once again. Don't forget to reset the cycleComplete var to false.
Perhaps it sounds more complex but gives you control of whether you want to run one more cycle or not. If not, then you can change the texture and run the cycle again.
Alternatively you may leave it as it is and only change the texture(s) after making sure the sprite is outside the visible area, e.g. its Y position is > view size height.
In SpriteKit you can use wait actions with completion blocks. This is more straightforward than using a NSTimer.
So, to answer your question - when using actions for moving the sprites on-screen, you should not change the sprite's position at any time - this is what the actions do. You only need to make sure that you update the texture when the position is off-screen. When the time comes, obviously some of the sprites will be displayed, so you can't change the texture of all 3 at the same time. For that you may need a helper variable to check in your update cycle (as I suggested above) and replace the textures when the time is right (sprite Y pos is off-screen).

Scenekit rotate camera around a scene

I have a simple SceneKit project in Swift where I add two objects to a scene: a ball and a box.
I want to be able to:
pan with 1 finger to rotate the camera around the scene
move an object by clicking on a ‘Move’ button, selecting an object, and dragging it to a new position (via a PanGesture)
I want this with allowsCameraControl = false, because I cannot do gesture 2) with it enabled, and I want to add further panGestures down the line.
I am stuck because I am unable to get the camera to rotate around the scene. I have the camera looking at one of the objects with a constraint:
let constraint = SCNLookAtConstraint(target: globalSCNNode)
constraint.gimbalLockEnabled = true
globalCameraSCNNode.constraints = [constraint]
but a panning gesture like this does not do anything:
func panGesture(sender: UIPanGestureRecognizer) {
let translation = sender.translationInView(sender.view!)
var action = SCNAction.rotateByX(0, y: 0.5, z: 0, duration: 0.1)
globalCameraSCNNode.runAction(action)
Can someone help?
Thank you,
The LookAt constraint will override your rotation commanded in panGesture. Try removing that constraint.
I'm guessing your goal is to rotate the camera around the center of the scene, otherwise, it wouldn't be a rotation, but a movement.
Please have a look at my answer here which is based on this answer

creating sprite kit game in swift. adding a character to the scene

I am creating a game and I set up the background already. Now when I put in the code to add the character to the scene it does not appear. This is the code I used to try and get the character to appear on the screen. Apparently my character shows up behind the background. Is there another way to get the character to show in front of the background?
func createPlayer() -> SKNode {
let playerNode = SKNode()
playerNode.position = CGPoint(x: self.size.width / 2, y: 80.0)
let sprite = SKSpriteNode(imageNamed: "Pig")
playerNode.addChild(sprite) return playerNode }
In order to make the character appear in front of the background image you need to use zposition:
//You need to choose a high zposition number to make sure it shows up in front.
playerNode.zPosition = 1
// you should set the background images z position using the same method to something below 1
I would also recommend using this to add your character to the scene:
self.addChild(playerNode)

How can I stop a SKAction.repeatActionForever action at a action boundary?

I have a sequence of SpriteKit actions that I create and then repeat forever on a node, but that I want to stop eventually. My sequence rotates a disk left, right, and left returning to the original rotation before starting again. However, when I remove the action, it stops without completing and so the original rotation is not restored.
I could save the original rotation state and restore it, but I want to know is there is a way to tell SpriteKit to only interrupt the action at the sequence boundary?
func wiggle() -> SKAction {
let wiggleLeft = SKAction.rotateByAngle(+0.04, duration: 0.1)
let wiggleRight = SKAction.rotateByAngle(-0.08, duration: 0.2)
let wiggleBack = SKAction.rotateByAngle(+0.04, duration: 0.1)
let wiggle = SKAction.sequence([wiggleLeft, wiggleRight, wiggleBack])
let wiggleForever = SKAction.repeatActionForever(wiggle)
return wiggleForever
}
disk.runAction(wiggle(), withKey: "wiggle")
...
disk.removeActionForKey("wiggle") // unfortunately stops mid-wiggle
Add the following code after disk.removeActionForKey("wiggle"):
disk.runAction.rotateToAngle(/*desired final angle of rotation*/)

Resources