Randomized Attack Animation RPG - unity3d

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

Related

how do i play an animation after colliding with a gameobject for x seconds in unity3d?

I was wondering how to play an animation after a certain amount of seconds but only if its been colliding with something, if it wasn't colliding anymore it would reset the seconds until it collides with the other object again.
I'm not sure if I understand correctly. It sounds to me like what you want is
Check if your object collides
Start a timer / countdown
If the timer finishes do something like play your animation
If the collision ends cancel the timer (if still running)
You could use e.g.
// The delay to wait before triggering the animation
// Adjust via the Inspector
public float delay = 3;
// Holds the currently running countdown routine
private Coroutine _currentRoutine;
private void OnCollisionEnter(Collision collision)
{
//ToDo: Eventually filter the collisions
// Is the routine already running? -> do nothing
if(_currentRoutine != null) return;
// Start a new routine
_currentRoutine = StartCoroutine(PlayAnimationAfterDelay());
}
private void OnCollisionExit(Collision collision)
{
//ToDo: Eventually filter the collisions the same way as in enter
// Is there still a routine running? -> cancel it
if(_currentRoutine != null) StopCoroutine(_currentRoutine);
}
private IEnumerator PlayAnimationAfterDelay()
{
yield return new WaitForSeconds(delay);
//Todo: Whatever you need to do to play the animation
// Never sure if this is needed but just to be sure reset when finished
_currentRoutine = null;
}
See
Coroutines
StartCoroutine / StopCoroutine
OnCollisionEnter / OnCollisionExit
WaitForSeconds

How to wait until animation is finished when using Animator?

This is a beginner question that should be available from the first google result but I can't find it.
I have this method that plays an animation using an Animator:
IEnumerator popDownAnimation(string c)
{
animator = GetComponent<Animator>();
animator.Play("Pop Down Animation");
yield return new WaitUntil(() => animator.GetCurrentAnimatorStateInfo(0).normalizedTime > 1 && !animator.IsInTransition(0));
animator.Play("New State");
}
Can't remember where I got it from, but yield return new WaitUntil(() => animator.GetCurrentAnimatorStateInfo(0).normalizedTime > 1 && !animator.IsInTransition(0)); isn't working. It's instantly jumping from "Pop Down Animation" to "New State", so the "Pop Down Animation" doesn't have a chance to play.
Try this
IEnumerator popDownAnimation(string c)
{
animator = GetComponent<Animator>();
animator.Play("Pop Down Animation");
float animationLength = animator.GetCurrentAnimatorStateInfo(0).length;
yield return new WaitForSecondsRealtime(animationLength);
animator.Play("New State");
}
However, like #NotVeryGoodAtThis mentioned, I wouldn't do this in code unless you have a specific reason why you can't make it work using animation events or using parameters within the animator to transition between animation states. Designing your animation using Unity's Animator window and using states, parameters, and transitions will be easier to manage than doing it through code, especially as your animators become more complicated.
I would suggest using Animation events to do whatever you need. Just remember to make a public method and add the animation event at the end of your animation.

Animator not animating a runtime instantiated child (UNITY)

I have a GameObject representing a room. As childs of this room I have 3 characters. Room has an animator component that control the synchronized animation of all characters.
The problem is I have another character that is instantiate from a prefab at runtime and this doesn't animate.
I tried several things:
//NOT WORKING
IEnumerator Start()
{
room.SetBool("e8", true);
yield return new WaitForSeconds(6);
...
}
//NOT WORKING
IEnumerator Start()
{
room.Rebind();
room.SetBool("e8", true);
yield return new WaitForSeconds(6);
...
}
//WORKING
IEnumerator Start()
{
yield return new WaitForEndOfFrame();
room.Rebind();
room.SetBool("e8", true);
yield return new WaitForSeconds(6);
...
}
The last try is working but the character looks stopped for an instant and suddenly jumps to the animation. I want the character animating from the beginning like the other characters.
My animator:
Simple solving
With current animator settings, this behavior is normal. You need to change your current animator using Any State section because when you use changing state from other states, Animation needs time to change your states. And for start Animator needs time for initializing, but if you will use Any State this will work.
Try doing something like this:
Entry different states
Either you can use what official documentation offers to you:
You can then add additional transitions from the Entry node to other states, to control whether the state machine should begin in a different state.
https://docs.unity3d.com/2019.4/Documentation/Manual/StateMachineTransitions.html
For example:

Unity Mecanim. How to reset animation before disabling a gameobject

Unity 2019.3.7f1
..
The artist I'm working with has made some animations for some UI stuff using the mecanim system Animator component.
I fire the animation with
Animator.SetTrigger("ParamaterName")
The gameobjects with the animator components, which are being animated, can get disabled by some of my scripts.
This can happen while the animation is playing..
So if the animation starts playing, and lets say the animation has made a button get bigger, then the gameobject gets disabled. When the gameobject is re-enabled (the animation is no longer playing) it is still big..
Is there a way to tell the Animator to go back to normal?
I have tried stuff like this in onEnable and OnDisable in a script on the GameObject
Animator.keepAnimatorControllerStateOnDisable = false
Animator.Play("normalState",0,0f);
Animator.playbackTime = 0f;
Animator.Update(0f);
This mecannim thing just seems like a black box as to how it works. I'm not familiar with it as I've always just used my own library to animate stuff that I've been using for donkeys years.
EDIT:
This was the solution...
private void Start()
{
Animator.keepAnimatorControllerStateOnDisable = true;
}
private void OnDisable()
{
Animator.Play("normalState",0,0f);
}
Make sure your keepAnimatorControllerStateOnDisable isn't set to true. Instead, you should set to false to clear the current state of the Animator controller
Animator.keepAnimatorControllerStateOnDisable = false
This way whenever you disable an object with an Animator, the Animator states and all its parameters go back to default.
Update
From your comments was able to understand the default is to have the button bigger. So, it's actually the other way around (set it to true, instead of false).
As you mention, write the following code
private void Start()
{
Animator.keepAnimatorControllerStateOnDisable = true;
}
private void OnDisable()
{
Animator.Play("normalState",0,0f);
}

Unity timeline, playing animation clips and controlling playback on events

I'm wondering if I can get some help on understanding how to use the timeline
I plan to have the character walking along a path with triggers that will activate the timeline,
I have set up a simple state machine on the monkey
which would be triggered by the path to play the timeline, that part works fine and I can see the events firing
How could I play part of the timeline when triggered (is that even possible?)
where the entire sequence is held Walk>Jump>Walk>Look>Walk>Idle
Or what would I need to do to be able to achieve playback of a clip/or part of a sequence of clips
On the monkey:
public enum State
{
WalkHappy,
Look,
Jump,
}
IEnumerator WalkHappyState()
{
Debug.Log("WalkHappy: Enter");
**//walk sequence with timeline here??**
while (state == State.WalkHappy)
{
yield return 0;
}
Debug.Log("WalkHappy: Exit");
NextState();
}
Called on the paths event listener when triggered
private void OnCustomEvent(string eventname)
{
if (eventname.Contains("MonkeyRunningJumpUp"))
{
GameObject tempMonkey = GameObject.FindGameObjectWithTag("Player");
Monkey tempMonkeyState = tempMonkey.GetComponent<Monkey>();
tempMonkeyState.state = Monkey.State.Jump;
}
}
I have seen its possible to control where the playback starts on a timeline, but I don't know how to force a range/clip to play then pause at the end of the clip rather than the end of the entire sequence
THE question is.. how to stop/pause at end of a clip to resume later?
playableDirector = myPlayableDirectorGO.GetComponent<PlayableDirector>();
Debug.Log(timeLinePosition);
playableDirector.time = timeLinePosition / ((TimelineAsset)playableDirector.playableAsset).editorSettings.fps;
playableDirector.RebuildGraph();
playableDirector.Play();
with thanks Natalie :)
First of all, I recommend to install Default Playables if you haven’t done it yet.
Use Timeline Signals and markers to do something at the end of timeline or at certain point. Also, there is PlayableDirector.stopped event.
You can use pause and resume methods;