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
Related
I have a problem with animation as I want the same animation with different end points, to be called upon finishing the previous animation and to be executed smoothly without slight stop.
Let me elaborate with a sample code and comments.
Func animate()
{
Start animation(completionHandler: {
If condition is true
Repeat the animation and check after it finishes
}
}
but the problem with this code is that, between finishing the first animation and starting another one, there will be a split second stop which is not smooth movement. For example, I have a view on zero point to move with animation to point A (x:100,y:100), and then it will receive point B (x:500, y:900), after it reaches point A it will stop a split second then move to point B. Since the points are Dynamic, I cannot know if there is a new point or not, and if I did i will not know the coordinates beforehand, so I need to keep the logic dynamic and keeps the animation smooth between points.
I tried many logics and results were not the thing I wanted. problems I faced toward a smooth queued animation are:
I tried UIView.animate in the following:
a. I tried setting an animation duration let's say 2 sec, then after it finishes, it calls animation again within completion hander/closure/block, but it will result in a less than second stop between the two animations.
b. I tried to start 2 animations, the second is delayed with the exact duration go the first animation, but results also brief stop between 2 animations.
2.I tried animation with keyframes, but since I don't know all animations in advance, this won't work.
Can you help please with my approach toward a smooth dynamic animation?
Your own pseudocode answers your question. Just test whatever your condition, and if you need to animate again, work out the new start and end points and animate again:
func animate(start:Int, end:Int) {
UIView.animate(withDuration: 5, animations: {
// animate here based on new start and end
}, completion: { _ in
var weShouldDoItAgain = false
// figure out if we should do it again
// if so, change weShouldDoItAgain to true
if weShouldDoItAgain {
var start = start // and now change it to new value
var end = end // and now change it to new value
self.animate(start:start, end:end)
}
})
}
Just change all the values and types to suit your use case.
I'm struggling with pause moment in my game.
I want to make button which perform pause logic and transition me to another scene with save action to sceneManager that holds gameScene.
Everything works fine but I can't make pause all actions my scene has. I read quite a lot of themes but still no luck.
The only think that really pauses any movement on my scene is
self.scene.view?.isPaused = true
But it pauses my transition as well.
I have spawning enemies, power-ups, shots and background stuff. So if I translate to other scene with this code:
transition.pausesOutgoingScene = true
transition.pausesIncomingScene = true
I see that objects and actions are not paused as well!
How to get pause logic work?
I added all nodes I want to pause to the same SKNode instance and after that I can just write:
someNodeWithSpritesToPause.isPaused = true
physicsWorld.speed = 0
This code solves my problem.
Thank you!
In my game I have a node that the player controls with the PanGesture. When the PanGesture function is called, it adds its velocity to that of the Node in question, making it move.
However, I have encountered a problem where the Node (under specific yet inconsistent circumstances) moves slower and not as smoothly as intended.
When I start the application it (sometimes) has the correct movement speed. As soon as I "die", and restart (remove all nodes and recognizers, restart the scene, remake all nodes and recognizers) the node does not move at the correct speed.
This is where things get weird.
After restarting the game and seeing that the node has the incorrect speed, I can access the "swipe up menu" and then promptly dismiss it, at which point the node will revert back to its intended speed. This however, does not always work. On top of that, I can leave the scene to have the same thing happen. This happens even less frequently.
This is the code I use to make a GestureRecognizer:
let Panned:UIPanGestureRecognizer = UIPanGestureRecognizer(target: self, action: #selector(GameScene.Panned))
view.addGestureRecognizer(Panned)
These lines are only run once per scene. (Making multiple GestureRecognizers causes the entire scene to experience dramatic frame drops each time the screen is touched.)
This is the code which adds the velocity of the movement to the Node:
func Panned(sender:UIPanGestureRecognizer){
velocity = sender.velocity(in: view)
if movement == true{
Node.position.x = Node.position.x+(velocity.x/100)/1
Node.position.y = Node.position.y-(velocity.y/100)/1
}
}
This function is called each time the PanGestureRecognizer detects a "pan" movement.
This is my restart function, which prepares the scene for the next instance of the game:
func restartScene(){
removeAllChildren()
removeAllActions()
createScene()
}
removeAllChildren will kill my stuttering node, and createScene() will revive it again. (It should be noted that createScene() is the same function used at the initial start of the game, and can therefor not be related to the problem.)
The restart is however where my problem starts. This function is missing certain (in my opinion) irrelevant lines that manipulate certain values and handle labels.
The main gist of my problem is as follows:
Do I fully understand GestureRecognizers?
What effect does the "swipe up menu" have on my current scene? Does it clear/restart certain things, or does it simply pause my scene?
Edit:
The most confusing thing about the problem is how it arises after a "restart", when the restart process does not touch the PanGesture in anyway. I'm considering remaking the game to create a version where the problem isn't present, but this leaves the problem unsolved.
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
I have tried many times to implement a D-Pad for my new maze game, but am having trouble doing so. I am doing it in the style of 4 UIButtons and if you press one, it moves another image up, down, left or right. I have tried using the quartzcore and CAAnimation, but only know limited code.
I have the methods and the buttons declared, but cannot code a working one.
I was doing:
-(IBAction)Up:(id)sender{
CGPoint origin1 = self.Player.center;
CGPoint target1 = CGPointMake(self.Player.center.x, self.Player.center.y-124);
CABasicAnimation *bounce1 = [CABasicAnimation animationWithKeyPath:#"position.y"];
bounce1.duration = 0.1;
bounce1.fromValue = [NSNumber numberWithInt:origin1.y];
bounce1.toValue = [NSNumber numberWithInt:target1.y];
[self.Player.layer addAnimation:bounce1 forKey:#"position"];
}
The ghost moves down, but bops back up immediately. I've been stumped for hours and it may be glaring me in the face, but please understand my noobishness.
In Core Animation, there are two layer hierarchies: one of model layers and one of presentation layers. Presentation layers are what you see; model layers are what you often interact with in code. This separation is valuable because it lets you create implicit animation -- e.g. set the position of a layer, and it'll animate to the new spot. (But if you assign a value to position, you want that to be the value you read back immediately thereafter, even if the animation is still in progress.)
When you use addAnimation:forKey:, you're affecting the presentation, not the model. So, during the animation, you see the Player layer moving, but that layer is "really" still where you left it. As soon as the animation ends, it's removed from the layer, so the presentation matches the model again -- you see it at its original position.
If you want the model position to change as well, you need to change it separately:
self.player.layer.position = CGPointMake(self.player.layer.position.x, target1.y);
You can either update the model immediately after adding the animation (you'll see the change after the animation completes), or use a completion block to schedule it at the end of the animation. There are subtleties to consider for either approach.
Often, though, if you just want to animate a specific change like that, especially for the main layer of a UIView, it's simpler to use the implicit animation API on UIView:
[UIView animateWithDuration:0.1 animations: ^{
self.player.center = CGPointMake(self.player.center.x, target1.y);
}];
This will both update the model value and create and run an animation for moving from the current position to the target position.
BTW: It helps to follow Cocoa code style conventions: use initial caps only for class names or other type names, and lowercase for variable/property/method names.