I have an idle player animation and I want to do a smooth transition between some animations. The idle animation is the default one and from that transition I want to be able to switch to another state(let's say fight) and then back to idle. The code for the idle character animation is currently like :
self.addChild(playerAnimation)
playerAnimation.runAction(SKAction.repeatActionForever(
SKAction.animateWithTextures(playerAnimationManager.idleManAnimation.textureArray, timePerFrame: 0.1)))
Now, this is scheduled to go on forever for now, but I would need to intercept it and add a new animation on top of that (which is the same character, in a new state). I was thinking that I should stop the idle animation, switch to the new one and then back to idle when finished but I am not convinced that this is the best way of chaining animations and I haven't really found a good resource explaining how to go about it.
Any suggestions ? Thanks !
Depending on how short your texture array is, you might be able to do this.
I will try to explain without code seeing how I use objective C and you use Swift
First make a property or variable that can be called by any subroutine in this class file. It should be Boolean and should be set to NO. You could call it idleFlag.
Next make a method that changes the animation to fight mode. This change would be by removing the idle animation and replacing it with the fight animation. This method also set's idleFlag to NO. Let's call the method "beginFightAnim"
And last, in your repeatActionForEver idle animation, right after your animateWithTextures animation, add a runBlock animation. In this block define a static variable (one that will be remembered in the calling of the block over and over) and increment it by +1, add an "if statement" that looks something like this -> if (my_static_var == number_of_frames_in_texture_animations && idleFlag). And in the "if statement" set the static variable to 0 and call "beginFightAnim"
After this all you have to do to change the animation is set idleFlag to YES.
Hope it works!
If you have any problems, please comment below.
I want to do a series of examples to make it easier to understand the matter. Suppose you have your node called playerAnimation, a typical atlasc with a plist where you have
player-idle1.png
player-idle2.png
player-idle3.png
...
If you want to intercept in real-time your animation to know what frame is running in that moment you can do:
override func update(currentTime: CFTimeInterval) {
if String(playerAnimation.texture).rangeOfString("player-idle") != nil {
print(String(playerAnimation.texture))
}
}
At this point, after you have assigned a "key" to your action (withKey:"idleAnimation") you could stop your specific idle action when you preefer to start the other next animation.
Another good thing is to build a structure to know everytime your player status and modify this variable each time you launch a new action or animation:
enum PlayerStatus: Int {
case Idle = 1
case Fight = 2
case Run = 3
case Jump = 4
case Die = 5
...
}
var currentStatus: PlayerStatus!
Related
I have monster named Fungant in my 2D platform game.
It can hide as mushroom and rise to his normal form.
I try to handle it in code, but I don't know how to get information about finished animation (without it, animation of rising and hiding always was skipped).
I think the point there is to get info about complete of the one of two animations - Rise and Hide.
There is current code:
if (
fungantAnimator.GetCurrentAnimatorStateInfo(0).IsName("Fungant Rise")
)
{
fungantAnimator.SetBool("isRising", false);
isRisen = true;
fungantRigidbody.velocity = new Vector2(
walkSpeed * Mathf.Sign(
player.transform.position.x - transform.position.x),
fungantRigidbody.velocity.y
);
}
if (fungantAnimator.GetCurrentAnimatorStateInfo(0).IsName("Fungant Hide"))
{
fungantAnimator.SetBool("isHiding", false);
isRisen = false;
}
I try this two ways:
StateMachineBehaviour
I would like to get StateMachineBehaviour, but how to get it?
No idea how to process this further.
AnimationEvents
Tried to do with animation event but every tutorial have list of functions to choose (looks easy), but in my Unity I can write Function, Float, Int, String or select object (what I should do?).
I decided to write test() function with Debug only inside, and create AnimationEvents with written "test()" in function field, but nothing happens.
Same as above, no more ideas how to process this further.
I personally would use animation events for this. Just add an event to the animation and then the event plays a function after the animation transition is finished.
For more information on how to use animation events you can click here.
This post may also help you to add an event when the animation is finished.
I hope this answer helps you to solve this problem.
Any way to show Game over screen only after the death animation of player is played text
Check in update if animation is playing or not. if animaion.IsPlaying() return false means your animation is finish and then you show your game over screen.
in your player script (or whatever script tells your die animation to play), go below the line where the die animation plays, then write this line of code: StartCoroutine(ExampleCoroutine());. Make sure you replace the 'ExampleCoroutine' part with the name of the IEnumerator method (more on that in the next sentence). Next, go below the function where you typed StartCoroutine(ExampleCoroutine());. and make an IEnumerator method. Inside it type yield return new WaitForSeconds(1f);. and replace the 1 with how long it takes for the die animation to finish. Lastly, below that line, activate your game over screen. Hope this helped a bit!
If you want to attach code functions to animations, you can use animation events to call functions at very specific times during (or at the end of) animations.
If you prefer, you can also use events to transmit animation status from inside your animator controller using state machine behaviours.
You can use a variable playeranimationfinised and use an IEnumerator with WaitUntil like this
using System;
using UnityEngine;
public IEnumerator gameover()
{
//function to check animation finished
Func<bool> animationfinished = () => playeranimationover;
//wait until playeranimationover is true
yield return new WaitUntil(animationfinished);
//show gameoverscreen
}
Is there a way to raise a flag on the Navmesh navigation end, or to use a callback when it finish?
I want to run a function when my objection reach to the desire position, I can check on every frame update if the navigation has finished but I want a more elegant and efficient solution.
Thanks.
It depends what you mean by "ends", if you want to know when a path is no longer available for the NavAgent, you can use pathStatus, for example you could write:
if(myNavAgent.pathStatus != NavMeshPathStatus.PathComplete) {
// Do stuff
}
If you me reaching a location such as a "waypoint" you can use myNavAgent.remainingDistance then check if it is less than a certain value.
Here's the unity NavAgent scriptingAPI doc link: https://docs.unity3d.com/ScriptReference/AI.NavMeshAgent.html
If this doesn't work, let me know!
I have Thought the same question and i couldn't find a callback, However i managed to solve with triggers. For example if my agent goes point a, i put trigger that position and when my agent touches it i stopped agent.
I'm looking for a solution for my problem:
I have defined a animation as SCNAction with the action editor in Xcode. Now I want to control it with a slider. So basically manipulating the slider shows a specific time/frame in the animation.
Right now, I can just pause/play the animation or reset it to the beginning. I achieved this with Swift code addressing the SCNAction from the action editor.
As far as I researched, there is no possibility
to animate a SCNAction to a specific time immediately and stop right there. That would work.
I'm also interested in other attempts if this is not achievable with SCNActions. It may be that this is not realizable at all. Thank you.
I found a solution: I loaded a CAAnimation from a .dae file(For this look here:https://stackoverflow.com/a/24924702/9625548, bit outdated but still usable).
With the SCNAnimationPlayer I can address the CAAnimation with a key. With the following
code I can control my animation with a UISlider:
let moment: TimeInterval = TimeInterval(self.animationTimeSlider.value)
guard let animationPlayer: SCNAnimationPlayer = node.animationPlayer(forKey: key) else{
return
}
animationPlayer.animation.timeOffset = moment
animationPlayer.play()
animationPlayer.speed = 0.0
Basically, I read the thumb and convert it to TimerInterval. I'm looking for the SCNAnimationPlayer and aborting if no SCNAnimationPlayer is found for the given key. Then I set the .timeOffset to the moment I want to display. With play(), the animation starts from the beginning + .timeOffset. Finally, set the .speed to zero. the animation is not running but shows the moment.
Attention: If you let your animation running and also manipulating it in that way, you have to reset it. Otherwise it is getting messed up. You can catch that case with a boolean that saves if you let your animation run normally. Then you know you have to reset it before manipulating with the slider. For example like this:
node.removeAllAnimations()
node.addAnimation(animation, forKey: key)
self.sceneView.scene.rootNode.animationPlayer(forKey: key)?.paused = true
I have two animations of a goalkeeper with two different states (with ball and without ball). After keeper jumps I want to switch to a different action but I want to stay at the same frame index.
I mean if goalkeeper collides with the ball in the frame 19, the second action should start at frame 19 too.
I'm still not quite sure if I completely understand what your trying to do but I can help you with that action. If you want to run a check every frame you should do somethng like this
At the end of your scenes init method add:
` [self schedule: #selector(tick:) interval: 0.3f];
-(void)tick:(ccTime) dt {
if(CGRectIntersectsRect(goalKeeper.textureRect, ball.textureRect) {
[goalKeeper stopAction: myAction]; //where myAction is previously defined
} //now you can start the new action because you're using a timer, it'll be the same frame
That should do it. I hope that helps, if I still didn't quite answer your question feel free to clarify what in more vivid what exactly you're going for. `