Check if animation clip has finished - c#

This is the simple code inside of update. sadly it is not working for me. Even I have set Wrap Mode to clamp Forever.
if (trainGO.GetComponent<Animation>()["Start"].time >= trainGO.GetComponent<Animation>()["Start"].length)
{
blackTrain.SetActive(true);
redTrain.SetActive(false);
}
How can I check that animation clip have finished so that I can do some work? I don't want to use WaitForSeconds method, because I am working on Time and each time a new animation will be played,

Basically like this ...
PlayAnimation(); // Whatever you are calling or using for animation
StartCoroutine(WaitForAnimation(animation));
private IEnumerator WaitForAnimation ( Animation animation )
{
while(animation.isPlaying)
yield return null;
// at this point, the animation has completed
// so at this point, do whatever you wish...
Debug.Log("Animation completed");
}
You can trivially google thousands of QA about this example, http://answers.unity3d.com/answers/37440/view.html
If you do not fully understand coroutines in Unity, step back and review any of the some 8,000 full beginner tutorials and QA regarding "coroutines in Unity" on the web.
This is really the way to check an animation is finished in Unity - it's a standard technique and there's really no alternative.
You mention you want to do something in Update() ... you could possibly do something like this:
private Animation trainGoAnime=ation;
public void Update()
{
if ( trainGoAnimation.isPlaying() )
{
Debug.Log("~ the animation is still playing");
return;
}
else
{
Debug.Log("~ not playing, proceed normally...");
}
}
I strongly encourage you to just do it with a coroutine; you'll end up "in knots" if you try to "always use Update". Hope it helps.
FYI you also mention "actually the scenario is: at specified time (Time/clock separately running). I have to play an animation thats why i am using update to check the time and run animation accordingly"
This is very easy to do, say you want to run the animation 3.721 seconds from now...
private float whenToRunAnimation = 3.721;
it's this easy!
// run horse anime, with pre- and post- code
Invoke( "StartHorseAnimation", whenToRunAnimation )
private void StartHorseAnimation()
{
Debug.Log("starting horse animation coroutine...");
StartCoroutine(_horseAnimationStuff());
}
private IENumerator _horseAnimationStuff()
{
horseA.Play();
Debug.Log("HORSE ANIME BEGINS");
while(horseA.isPlaying)yield return null;
Debug.Log("HORSE ANIME ENDS");
... do other code here when horse anime ends ...
... do other code here when horse anime ends ...
... do other code here when horse anime ends ...
}

Related

unity make a delay between switching panels

I currently have such a piece of code in panelmanager where I open certain panels (I have a similar piece of code for closing panels). But I wanted to make sure that after pressing the button, there was no instant switching between panels, but with a delay. I read that it can be done with coroutines, but I haven't succeeded yet, since I'm probably using it incorrectly. Please tell me how I can implement such a delay correctly, I'm a complete noob..
public void OpenPanel(string name)
{
switch (name)
{
case nameof(MainMenu):
StartCoroutine(CoroutineSample());
MainMenuPanel.gameObject.SetActive(true);
break;
case nameof(LevelsPanel):
StartCoroutine(CoroutineSample());
LevelsPanel1.gameObject.SetActive(true);
break;
}
}
private void Start()
{
StartCoroutine(CoroutineSample());
}
private IEnumerator CoroutineSample()
{
yield return new WaitForSeconds(2);
}
You have to call the
MainMenuPanel.gameObject.SetActive(true);
inside the coroutine after the yield return line.
As it is in your code the StartCoroutine just starts the coroutine and then continues immediately with the next line. So it won't wait. And the WaitForSeconds call doesn't help anything because after the 2 seconds the coroutine doesn't do anything.

How to create paint on the floor every time my Unity character dies?

red area in the image. The thing about my character dying in combat. Here's how to live paint on the floor when every character dies.
i tried this but unfortunately it didn't work
The first part of code contains void RunTowardsTarget(). Do you call this void one time or every frame? It looks like you call it one time but there is if statement in this method so the condition will compare one time.
I think you need to use coroutine.
public IEnumerator RunTowardsTarget()
{
while(Vector3.Distance(transform.position, targetRunner.position) > 1)
{
targetRunner.position = targetRunner.position + speed;
yield return null;
}
//your destroy calls
onRunnerDied?.Invoke();
Destroy(targetRunner.gameObject);
Destroy(gameObject);
}

How to repeat function while button is held down (New unity input system)

Trying to repeat the function function OnAttack() continuously while a button is being held down.
Basically I'm looking for an equivalent to Update() { GetKeyDown() {//code }} But with the input system.
Edit: using a joystick, cant tell what button is being pressed.
Okay I solved it by using "press" in the interactions and giving that The trigger behavior "Press and release", then did
bool held = false;
Update()
{
if(held)
{
//animation
}
else if(!held)
{
//idle animation
}
}
OnAttack() {
held = !held;
}
This way if I press the button held goes to true so it repeats the animation every frame, letting go makes "held" untrue and does the idle animation
Essentially, the function you assign to the button will be triggered twice per button press, once when it is pressed (performed), and once when it is released (canceled). You can pass in this context at the beginning of your function, just make sure you are using the library seen at the top of this script.
Now you can toggle a bool on and off stating whether or not the button is pressed, then perform actions during update dependent on the state of the bool
using static UnityEngine.InputSystem.InputAction;
bool held = false;
Update()
{
if(held)
{
//Do hold action like shooting or whatever
}
else if(!held)
{
//do alternatice action. Not Else if required if no alternative action
}
}
//switch the status of held based on whether the button is being pressed or released. OnAttack is called every time the button is pressed and every time it is released, the if statements are what determine which of those two is currently happening.
OnAttack(CallbackContext ctx) {
if (ctx.performed)
held= true;
if (ctx.canceled)
held= false;
}
This is paraphrasing a solution I created for a click to move arpg mechanic.
using System.Threading.Tasks;
using UnityEngine;
[SerializeField] private InputAction pointerClickAction;
private bool pointerHeld;
void Start()
{
pointerClickAction.canceled += ClickMouseMove;
pointerClickAction.started += PointerHoldBegin;
pointerClickAction.performed += ClickMouseMove;
pointerClickAction.canceled += PointerHoldEnd;
}
private void OnEnable()
{
pointerClickAction.Enable();
pointerPositionAction.Enable();
}
private void OnDisable()
{
pointerClickAction.Disable();
pointerPositionAction.Disable();
}
public async void ClickMouseMove(InputAction.CallbackContext context)
{
while (pointerHeld)
{
DoSomething();
await Task.Delay(500);
}
}
public void PointerHoldBegin(InputAction.CallbackContext context)
{
pointerHeld = true;
}
public void PointerHoldEnd(InputAction.CallbackContext context)
{
pointerHeld = false;
}
public void DoSomething()
{
//Your Code
}
In Task.Delay() you can insert your own polling rate in milliseconds, using Task.Yield() seems to be faster than Update so I don't recommend that, you should poll at the minimum the same delay as your physics/fixed update, having a higher delay gives a performance boost if you don't need a high amount of repetitions per loop. I set mine to 500 since I don't need my character to plot its navigation that often. In regards to TC, you would set the delay to something sensible e.g the attack's animation length, or whatever the delay rate would be for how many attacks can be performed per second.
If you want your project to scale you might want to avoid as much as possible assertions(i.e if functions) in your Update/FixedUpdate/LateUpdate functions as they are executed constantly. I recommand you to read this article about coroutines https://gamedevbeginner.com/coroutines-in-unity-when-and-how-to-use-them/
You can build coroutines which act as local update() functions which are executed only when needed. This will lead you to a better organization of your code and might boost performance in some cases.
For exemple in your case you could use something like this.
bool held = false;
Update()
{
/* Whatever you want but the least assertion possible */
}
IEnumerator RenderHeldAnimation()
{
while (held)
{
// held animation
yield return new WaitForFixedUpdate(); /* Will block until next fixed frame right after FixedUpdate() function */
// yield return null /* Will block until next next frame right after Update() function */
}
}
IEnumerator RenderIdleAnimation()
{
while (!held)
{
// idle animation
yield return new WaitForFixedUpdate(); /* Will block until next fixed frame right after FixedUpdate() function */
// yield return null /* Will block until next next frame right after Update() function */
}
}
OnAttack() {
held = !held;
if (held) {
StartCoroutine(RenderHeldAnimation());
} else {
StartCoroutine(RenderIdleAnimation());
}
}
As mentioned in another answer, the context.canceled is not called when using the Press And Release interaction. As a follow up for documentation purposes as this is a top Google result, to correctly use a bool held without doing a blind toggle (held = !held) which may end up with drift, you can access context.control.IsPressed() like the following:
void OnAttack(CallbackContext context)
{
held = context.control.IsPressed();
}
I encountered the same issue and this was the method that seemed to work for me
private float _moveSpeed = 3f;
private float _moveDirection;
private void Update()
{
transform.Translate(_moveSpeed * _moveDirection * Time.deltaTime * transform.forward);
}
public void Move(InputAction.CallbackContext ctx)
{
_moveDirection = ctx.ReadValue<float>();
}
For some odd reason,the hold interaction works properly in reading the input, but I still need the update function to implement the actual logic.
Can't really complain though, it works. Although I'd love to know why it happens this way.
Hopefully this can be of help to someone.
You can use a timer for that purpose, in combination with events KeyUp and KeyDown.
Please look at the following link. It is pretty much similar to your problem.
Link

Find time since a variable was changed

How would you find the time since a certain variable was changed? Take for example a boolean variable, how would you find the time since it was last changed? I want to use the boolean variable as a trigger (activating the trigger when it's true), but only after an exact, constant time (such as 0.5s) has passed since it was changed to true (it can only be changed from false to true).
Here is the code I have:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class hitRegistration : MonoBehaviour
{
AudioSource hitSound;
private bool hitState = false;
// Use this for initialization
void Start()
{
hitSound = gameObject.GetComponent<AudioSource>();
}
void OnMouseOver()
{
Debug.Log("Mouse is over game object.");
if (Input.GetKey(KeyCode.X) && hitState == false)
{
hitSound.Play();
hitState = true;
}
}
private void OnMouseExit()
{
Debug.Log("Mouse is no longer over game object.");
if (hitState == true)
{
// sound clip gets cut if the cursor leaves before its finished.
Destroy(gameObject);
}
}
// Update is called once per frame
void Update()
{
}
}
"OnMouseOver()" Is simply a function that is called when the mouse is placed over the game object in question. I want to delay destroying the game object until a certain time has passed.
First off, as noted in a comment, you are probably trying to solve this problem the wrong way and you are probably asking an "XY" question -- a question where you are asking a question about a proposed bad solution instead of asking a question about the actual problem you face.
To answer the question you actually asked, for better or worse: there is no way to associate behaviours with reading or writing a variable in C#, but you can associate behaviours with a property:
private bool hitState; // The "backing store".
private bool HitState
{
get
{
return hitState;
}
set
{
hitState = value;
}
}
You would then use HitState rather than hitState throughout the rest of your class.
Now you can add whatever logic you want that happens when the property is read or written:
private DateTime hitStateTime = default(DateTime);
private bool hitState; // The "backing store".
private bool HitState
{
get
{
return hitState;
}
set
{
hitState = value;
hitStateSet = DateTime.Now;
}
}
Now you know when it was set. And so on.
Unless you really need to keep track of how much time has passed on each single frame, one way to do what you are asking for is to use Unity Coroutines.
A coroutine is a method that runs in parallel with the main thread. To solve your question, you can first create a coroutine in the same script, that waits and then does the thing you want to have delayed. A couroutine in Unity is a method that takes up to one parameter and has an IEnumerator return type. You use yield return WaitForSeconds(t); inside the coroutine to have it delay for t seconds.
Then, once it's time to die, check if the mouse is currently hovering over the object with isHovered (set in your OnMouseOver/OnMouseExit methods). If it is, keep a note that it's time to die. If it isn't, then it can die immediately.
IEnumerator WaitToDie(float delaySeconds)
{
yield return new WaitForSeconds(delaySeconds);
// If the mouse is on the object, let OnMouseExit know we're ready to die
if (isHovered)
{
readyToDie = true;
}
// Otherwise, just die
else
{
Destroy(gameObject)
}
}
And then inside your OnMouseOver code, run the coroutine after starting the sound
void OnMouseOver()
{
isHovered = true;
Debug.Log("Mouse is over game object.");
if (Input.GetKey(KeyCode.X) && !hitState)
{
hitState = true;
hitSound.Play();
// we want to delay for half a second before processing the hit.
float delaySeconds = 0.5;
IEnumerator coroutine = WaitToDie(delaySeconds);
StartCoroutine(coroutine);
}
}
And in your OnMouseExit, let everything know that you're done hovering and check if it's past time to die or not.
private void OnMouseExit()
{
isHovered = false;
Debug.Log("Mouse is no longer over game object.");
if (readyToDie) {
Destroy(gameObject);
}
}
Altogether this code will have the object die when both the mouse is off the object AND the time has elapsed.
As a sidenote, I think you might want to revisit how you are checking for a hit, unless your really want to trigger from the player holding X and then moving the mouse over the object. If you intend to trigger any time X is pressed down while the mouse is on top, you might want to put the check in Update and check Input.GetKey(KeyCode.X) && !hitState && isHovered
keep a seperate variable(DateTime) and call it lastUpdate. then be sure to set it to DateTime.Now, each time the bool you're tracking is updated. then when you need to see how long its been you can just subtract:
DateTime lengthOfTime = DateTime.Now-lastUpdate;
from lengthOfTime you can now access how many days, hours, minutes, and/or seconds have passed.
im on my phone so take it easy on my pseudo-code.
good luck

Which to use Coroutine or Update to slow down animation

I want to do
gradually stop car animation as it collildes with another car
gradually speed up car animation as it exit the collider
And I have two ways to acheive this thing(means to run my logic in).
Update
Co-routine
for coroutine I used this
IEnumerator IncreaseSpeedGradually1(AnimationControlSpeed lastGOHitScript)
{
//stop if decrease speed in progress
StopCoroutine("DecreaseSpeedGradually");
float decrementValue = ((lastHitVehicleSpeed / 2) * 2);
while (lastGOHitScript.Speed <= lastHitVehicleSpeed)
{
lastGOHitScript.Speed += decrementValue * Time.deltaTime;
yield return 0;
}
//setting speed to the last speed
lastGOHitScript.Speed = lastGOHitScript.iniSpeeed;
}
While for update I just make this criteria:
if (carAnimState == carAnimationState.starting)
{
carAnimState = carAnimationState.running;
}
if (carAnimState == carAnimationState.stoping)
{
carAnimState = carAnimationState.running;
}
These two ways I know but i want to ask that which is right way to do this job? to slow down animation speed and hence get my objective? I guess corroutine later can be problematic in my game and what are the performance concerns?
Not perfectly clear what your goal exactly is. What you describe is clear, but the two code snippets you posted do completely different things. BUT, basically, for something this small I would use Update() as it's easier to debug if something blows up and there's nothing -theoretically- in your code that wishes "threading" / "side tasking" (which coroutines are for, basically: do something -most probably a side task- async).
As a side note, the code in your Update() can be optimized a little so it will look something like this:
if (carAnimState == carAnimationState.starting) {
carAnimState = carAnimationState.running;
} else if (carAnimState == carAnimationState.stoping) {
carAnimState = carAnimationState.running;
} //this way the 2nd "if" won't be unnecessarily evaluated when animstate was .starting
I'd also add a method instead and call that from Update(). The pre-compiler will inline it most probably (cannot be forced in Unity) and your code is more readable.

Categories