Animation Trigger set in IEnumerator causes Animation to get stuck - unity3d

private IEnumerator reviveCountdown() {
timeLeft = 5;
while (timeLeft >= 0) {
reviveAnim.ResetTrigger ("AdReviveTrigger");
reviveAnim.SetTrigger ("AdReviveTrigger");
reviveCountdownText.text = timeLeft.ToString();
yield return new WaitForSecondsRealtime(1.0f);
timeLeft--;
}
}
I'm using the above IEnumerator to show a timer that counts down from 5 to 0, and animates the text each time it counts down. The countdown works correctly. The problem I'm having is the animation IS TRIGGERED but never actually plays because it gets stuck on the animation state, as shown in the screenshot below. It sits in the AdRevive state for the entire 5 seconds but never plays. The transition from Idle is a simple trigger set in the code. The animation works if I play it manually in the Unity editor. Anyone know why it gets stuck?

I solved my own issue. The Time.timeScale was set to 0 because the game was paused during this animation, so it would never play. To fix, I set the animationUpdateMode to UnscaledTime.

Related

Unity: Animation won't play on trigger

Im having trouble to let Unity play my animations.
I gave my character an Animator. In this Animator I have this Animations:
The trigger between these Animations are working fine and are running correctly on play. The idle animation it self is played, while i can't get it to play the walk or the jump Animation in the game (both work fine in the inspector).
When on play the triggers are triggered, it even shows the blue bar under the walking or the jumping box, but the animation itself just wont be played in the game.
Here is the code I use to call the triggers:
void Update()
{
if (Input.GetKey(KeyCode.LeftArrow) || Input.GetKey(KeyCode.RightArrow)|| Input.GetKey(KeyCode.A)||Input.GetKey(KeyCode.D))
{
anim.SetBool("isWalk", true);
}
else
{
anim.SetBool("isWalk", false);
}
if (Input.GetKey(KeyCode.Space))
{
anim.SetBool("isJump",true);
}
else
{
anim.SetBool("isJump", false);
}
}
And the Transition Conditions (but these are working fine for me):
idle->jump: isJump =true,
jump->idle: isJump =fals,
idle->walk: isWalk =true,
walk->idle: isWalk =false,
screenshot of idle-> walk:
screenshot of walk->idle:
I hope someone can help me with this and please excuse my possibly bad english...
I had the same issue.
Solved it by unchecking 'Can Transition To Self' inside the transition pointing to the state that didn't play.

Active a Unity animation if certain conditions are met?

I'm developing a 2d game in Unity and I ran into a problem. When trying to create animation transitions for one of my animations for some reason Unity instantly or very quickly deactivates my animation(like less then a second after I activated it). However when I don't have a transition conected to the animation the animation works fine. However I need to be able to transition the gameobject back to the state it was in before the animation after a certain condtion is met and so I cant leave the animation running. Does anyone know how I can solve this problem?
Ive tried switching the way I activate the animations to see if it might be related to this by trying both a bool and trigger as the condition that determines when a transition should happen but the same problem arrises
THis is a rough outline of how I activate the animation
public class InteractControl : MonoBehaviour, IPooledObject
{
public static float timeLeft = 10f;
public void OnObjectSpawn()
{
anim = GetComponent<Animator>();
}
// Change this method to just be a temporary obstacle
void Update()
{
timeLeft -= Time.deltaTime;
if (timeLeft > 0)
{
anim.enabled = true;
}
if (timeLeft < 0)
{
anim.SetTrigger("FollowAd");
}
}
}
Ad Follow with transition:
Ad Follow without transition:
Transition Settings:
The animation should activate if timeLeft is bigger than 0 and deactivate as soon as it is smaller then 0 and return the the gameobject to the state it was in before the animation started. Right now if Ad Follow_ has no transition the animation works when called upon but doesn't deactivate. if Ad Follow_ has an empty state connected to it to which it should transition should
anim.SetTrigger("FollowAd");
be called like shown above it works for less then a second or not at all and then switches straight back to the default state. It should only switch back when timeLeft < 0.
You can create a public RuntimeAnimatorController animCon for your required animation. When your conditions are met. You can do anim.runtimeanimatorcontroller = animCon;.
And When the condition is false do this anim.runtimeanimatorcontroller = null;. Its not the best way but its quick and better then most.

Unity: LoadScene does not work when fired from timer

in my game, when the player dies, a dying sound is played and once the sound is over, the scene is supposed to be reloaded when the user still has enough lives.
Before I had the sound, the play died instantly upon calling the death() function:
public static void Death()
{
AddCoinScript.coinCounter = 0;
LivesScript.livesCounter--;
if (LivesScript.livesCounter > -1)//to get 0 live
{
Debug.Log("TIMER");
var currentScene = SceneManager.GetActiveScene();
SceneManager.LoadScene(currentScene.name);
}
else
{
//TO DO GameOver
}
}
This worked like a charm.
But now I added a death sound to it. Unfortunately, unity doesnt provide an event handler for when the sound is done playing (I want the scene to be reloaded not instantly anymore, but after the death sound is done playing), so I have decided to take it upon myself to just build a timer. The timer fires right after the death sound is over. This is what this function has become:
public static void Death()
{
AddCoinScript.coinCounter = 0;
LivesScript.livesCounter--;
PlayDeathSound();
System.Timers.Timer timer = new System.Timers.Timer();
timer.Interval = aSDeath.clip.length * 1000;
timer.Start();
timer.Elapsed += delegate
{
timer.Stop();
if (LivesScript.livesCounter > -1)//to get 0 live
{
Debug.Log("TIMER");
var currentScene = SceneManager.GetActiveScene();
SceneManager.LoadScene(currentScene.name);
}
else
{
//TO DO GameOver
}
};
}
As you can see, to make sure the timer REALLY fires, I set up a "debug.Log("TIMER")" to see, if it really works. And guess what: it does. The debug now shows "TIMER" in its console. But you know what doesnt work anymore? The two lines of code right beneath that.
var currentScene = SceneManager.GetActiveScene();
SceneManager.LoadScene(currentScene.name);
It's the same exact lines that worked just before - but when fired from the timer, they just get ignored? How is this even possible?
When I change it all back, it works again. Only when the timer fires the two lines, they get ignored.
This is totally odd or am I missing something? Thank you!
Okay I am not an expert on C# and delegate but apparently it creates a separate thread and you can only use SceneManager.GetActiveScene on main thread.
Since i am not so sure about delegate i will offer an easier solution. You can use a coroutine since you know how much you have to wait like this:
public void Death()
{
StartCoroutine(DeathCoroutine());
}
IEnumerator DeathCoroutine()
{
AddCoinScript.coinCounter = 0;
LivesScript.livesCounter--;
PlayDeathSound();
// wait for duration of the clip than continue executing rest of the code
yield return new WaitForSeconds(aSDeath.clip.length);
if (LivesScript.livesCounter > -1)//to get 0 live
{
Debug.Log("TIMER");
var currentScene = SceneManager.GetActiveScene();
SceneManager.LoadScene(currentScene.name);
}
else
{
//TO DO GameOver
}
}
What about using a coroutine ? You just start it when the player dies, and yield while your sound is still playing.

Randomized Attack Animation RPG

First of all, I'm quite noob with animator and animation systems in unity.
What I'm trying to achieve (and I was trying with Animator component) is a randomized attack only while I keep my mouse button pressed on the enemy and which completes the execution of the attack clip that is playing even if i release the button meanwhile.
I tried adding my 2 attack animations to a list and play it, with something like
anim.Play(Random.Range(0, list.Count))
...but I don'tknow if the problem is that while I keep pressed one animation cancels the other or what.
Therefore I prefer to ask, because I'm probably doing things in the wrong way.
Thank you.
Yes the issue is probably what you said: You have to wait until one Animation finished before starting a new one otherwise you would start a new animation every frame.
You could use a Coroutine (also check the API) to do that.
Ofcourse the same thing could be implemented also only in Update without using a Coroutine but most of the times that becomes really cluttered and sometimes even more complicated to handle. And there is not really any loss or gain (regarding performance) in simply "exporting" it to a Coroutine.
// Reference those in the Inspector or get them elsewhere
public List<AnimationClip> Clips;
public AnimationClip Idle;
private Animator _anim;
// A flag to make sure you can never start the Coroutine multiple times
private bool _isAnimating;
private void Awake()
{
_anim = GetComponent<Animator>();
}
private void Update()
{
if(Input.GetMouseButtonDown(0)
{
// To make sure there is only one routine running
if(!_isAnimating)
{
StartCoroutine(RandomAnimations());
}
}
// This would immediately interrupt the animations when mouse is not pressed anymore
// uncomment if you prefer this otherwise the Coroutine waits for the last animation to finish
// and returns to Idle state afterwards
//else if(Input.GetMouseButtonUp(0))
//{
// // Interrupts the coroutine
// StopCoroutine (RandomAnimations());
//
// // and returns to Idle state
// _anim.Play(Idle.name);
//
// // Reset flag
// _isAnimating = false;
//}
}
private IEnumerator RandomAnimations()
{
// Set flag to prevent another start of this routine
_isAnimating = true;
// Go on picking clips while mouse stays pressed
while(Input.GetMouseButton(0))
{
// Pick random clip from list
var randClip = Clips[Random.Range(0, Clips.Count)];
// Switch to the random clip
_anim.Play(randClip.name);
// Wait until clip finished before picking next one
yield return new WaitForSeconds(randClip.length);
}
// Even if MouseButton not pressed anymore waits until the last animation finished
// then returns to the Idle state
// If you rather want to interrupt immediately if the button is released
// skip this and uncomment the else part in Update
_anim.Play(Idle.name);
// Reset flag
_isAnimating = false;
}
Note that this way of randomizing does not provide things like "Don't play the same animation twice in a row" or "Play all animations before repeating one".
If you want this checkout this answer to a very similar question. There I used a randomized list to run through so there are no doubles

How to run multiple scene of unity just once during the launching of app

I want to know how to make something happen only once like a tutorial in a game which appears only when you first start your game and then when your game got saved to a further point it never appears again even when you close your game and start again.
Basically, I have seven scenes but I want to play scene 1-5 just one on the launching of the app and then next time when the user opens the app it directly jumps to the 6th scene
So I want to know how to make something happen only once in a game application.
I hope you get my point.
Well, this issue can be easily addressed using PlayerPrefs. You can call PlayerPrefs.SetInt() and PlayerPrefs.GetInt(). Like so:
public class SceneRouting : MonoBehaviour {
void Awake(){
// -1 means it is not the first time the player launches the game
// while 1 means it is the first time the player launches the game
int isFirstTime = PlayerPrefs.GetInt("isFirstTime", -1);
if(isFirstTime > 0){
// Here you can load any of these scenes 1, 2, 3, 4, 5
} else {
// Here you can load scene 6
}
}
}
You can set the isFirstTime variable anywhere in your game flow. Say, for example, when you call the Foo() method, you don't want the game to load scenes 1-5 next time it launches:
public void Foo(){
PlayerPrefs.SetInt("isFirstTime", -1);
}
Additionally, if you want your game to load scene 6 upon launch, you can set the isFirstTime value to 1
You could have a look at using PlayerPrefs. There are multiple ways of using them but this could be one lightweight solution for you.
An example of how to use them would be like this for you;
private void Awake()
{
int tutorialFinishedFlag = PlayerPrefs.GetInt("TutorialFinished", 0);
if(tutorialFinishedFlag == 0)
{
ShowScenesOneToFive();
}
else
{
ShowSceneSix();
}
}
And when you've finished showing them the first 5 scenes, you can set the PlayerPref like so;
PlayerPrefs.SetInt("TutorialFinished", 1);