How to set delay OnStepcomplete with dotween Unity? - unity3d

I want to set a delay after every dotween loop. Onstepcomplete method doesn't seem to work with Ienumorator and SetDelay method delay at the start of the Tween. How do I it?
void Movement()
{
transform.DOPath(path, moveduration, pathType, pathMode, 10).SetLoops(-1, LoopType.Yoyo).SetEase(ease).SetDelay(startdelay)
.OnStepComplete(StartCoroutine(Wait()));
}
private IEnumerator Wait()
{
yield return new WaitForSeconds(delay);
}

You need to put your tween in a sequence to do what you want.
DOTween.Sequence().SetDelay(1f).Append(transform.DOPath(XXX).OnComplete(XXX)).AppendInterval(1f).SetLoops(-1, LoopType.Yoyo).Play();

Related

How do I flip the gameobject at every Dotween loop?

transform
.DOPath(path, animDuration, pathType, pathMode, 10).SetLoops(-1, LoopType.Yoyo)
.SetEase(ease);
This is my simple dotween code to follow path. After every yoyo I want my object to flip or change direction towards its next path waypoint. Is there any dotween way of doing this?
DoTween has OnStepComplete().
DOTween:
Sets a callback that will be fired each time the tween completes a single loop cycle (meaning that, if you set your loops to 3, OnStepComplete will be called 3 times, contrary to OnComplete which will be called only once at the very end). More...
void Start()
{
transform
.DOPath(path, animDuration, pathType, pathMode, 10)
.SetLoops(-1, LoopType.Yoyo)
.SetEase(ease).OnStepComplete(FlipScpite);
}
private void FlipScpite()
{
_spriteRenderer.flipX = !_spriteRenderer.flipX;
}

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

Is it safe to start a new coroutine inside a current coroutine?

I have a grid-based game in which I programmed my movement script to move my game objects cell by cell. To achieve the cell by cell movement that I want, I had to use coroutines.
Here's a pseudo-code snippet of my code:
private Coroutine currentCoroutine;
public void Move(Vector3 velocity)
{
currentCoroutine = StartCoroutine(MoveCoroutine(velocity));
}
private IEnumerator MoveCoroutine(Vector3 velocity)
{
Vector3 endPoint;
Vector3 nextVelocity;
if(velocity == Vector3.Right)
{
endPoint = rightEndPoint;
// object should move left in the next coroutine
nextVelocity = Vector3.Left;
}
else
{
endPoint = leftEndPoint;
// object should move right in the next coroutine
nextVelocity = Vector3.Right;
}
while(currentPos != endPoint)
{
currentPos += velocity
yield return new WaitForSeconds(movementDelay);
}
currentCoroutine = StartCoroutine(MoveCoroutine(nextVelocity));
}
Basically what this does is move my object left and right. If it reaches the left edge already, I make it go right and vice-versa. I call the Move() from another script.
This code is working fine for me. However, I am not sure if starting a new coroutine inside a coroutine is safe, like what I did here. Are there any consequences when writing coroutines like this? I'm still getting used to the concept of coroutines.
Thanks
Starting a new coroutine at the end of a coroutine is safe. By the way, I noticed that your yield statement is only called inside a while loop. If there's any chance the while loop won't run at least once, you'll have an error; all coroutines must execute a yield statement.

Delaying Update() with Invoke

Is it possible to delay the effects of a script that is attached to a game object?
I have 2 characters in a scene, both receiving live motion capture data, so they are both animated identically. I have rotated one by 180 degrees and placed it in front of the other one, to create the impression that they are copying/mirroring each other. Each has several scripts attached to them, of course.
Now, I would like to be able to delay one character a bit (e.g. 1 sec) to make it look more realistic, as if one character is observing the other character and then copying its exact movements.
I am trying to achieve this by buffering the animation data (network data) that reaches the second character. Below, is the code snippet that animates the 1st character immediately:
void Update()
{
Vector3[] latestPositions;
Quaternion[] latestOrientations;
if (mvnActors.getLatestPose(actorID-1, out latestPositions, out latestOrientations))
{
updateMvnActor(currentPose, latestPositions, latestOrientations);
updateModel(currentPose, targetModel);
}
}
And below, is the code snippet that is supposed to animate the 2nd character but in fact there is no movement at all:
private Queue<Vector3[]> posQueue;
private Queue<Quaternion[]> rotQueue;
void Start()
{
posQueue = new Queue<Vector3[]>();
rotQueue = new Queue<Quaternion[]>();
}
void Update()
{
Vector3[] latestPositions;
Quaternion[] latestOrientations;
if (mvnActors.getLatestPose(actorID-1, out latestPositions, out latestOrientations))
{
posQueue.Enqueue(latestPositions);
rotQueue.Enqueue(latestOrientations);
if ((posQueue.Count > 10) && (rotQueue.Count > 10))
{
Vector3[] delayedPos = posQueue.Peek();
Quaternion[] delayedRot = rotQueue.Peek();
updateMvnActor(currentPose, delayedPos, delayedRot);
updateModel(currentPose, targetModel);
}
}
}
Isn't peek supposed to provide the 1st element in the queue, which here would be the one in the 11th place? Or is it because queue is not suitable for heavy animation updating?
EDIT: SOLVED
Added int delayedFrames = 30; to class variables (to replace number 10) and used Dequeue instead of Peek.
EDIT: Answer to #JTech's question isn't complete in this answer, look at the comments underneath his/her question for the final solution
I personally dislike WaitForSeconds, it has slowed my game way down once.
Try something like:
public List<Vector3[]> PreviousPositions;
public List<Quaternion[]> PreviousRotations;
private Vector3[] latestPositions;
private Quaternion[] latestOrientations;
void Start(){
PreviousPositions = new List<Vector3[]>();
PreviousRotations = new List<Quaternion[]>();
}
void Update(){
if (mvnActors.getLatestPose(actorID-1, out latestPositions, out latetOrientations))
{
updateModel(currentPose, targetModel);
//Add the position and rotationstuff to the 'buffer' lists here
Invoke("DoStuff", 1);
}
}
void DoStuff(){
updateMvnActor(currentPose, PreviousPositions[0] , PreviousRotations[0]);
PreviousPositions.RemoveAt(0);
PreviousRotations.RemoveAt(0);
}
OnApplicationQuit(){
CancelInvoke();
}
I don't think you can delay the Update method.
What you can use is WaitForSeconds, or you can test if 1 sec passed with an if statement based on Time.
Then execute the code to move the second character.
EDIT
I think a simple timer will do the job but you will need to take in account the 1 sec time when getting the latest positions/orientations.
public float delay = 1.0f;
private float timer = 0;
void Update()
{
Vector3[] latestPositions;
Quaternion[] latestOrientations;
timer += Time.deltaTime;
if (timer >= delay)
{
if (mvnActors.getLatestPose(actorID-1, out latestPositions, out latestOrientations))
{
updateMvnActor(currentPose, latestPositions, latestOrientations);
updateModel(currentPose, targetModel);
}
timer = 0;
}
}
Otherwise you can use InvokeRepeating with a delay as a parameter to repeat a method every X seconds.
To take only the actions made before the delay, you can register the time at the moment when it's done, 'remove' the actions made during the delay and take the actions made before it.
Or register the number of actions made by the first character during the delay and remove the same number of actions - the last ones - to your update on the second character. (seems easier/more simple)
Like this, I assume the second character will not do the last actions made during the delay, but will do it after.

How to add gui text at run time in unity

I have a problem to display a text at run time in my game for a second , so I want to know if its possible to add or remove a GUI label to the seen at run time ?
Do you mean something like this?
void OnGUI() {
if (textShouldBeShown) {
GUI.Label(new Rect(10f, 10f, 100f, 50f), "MyText");
}
}
GUI components get drawn on every frame. the OnGUI() function is just like the Update() function except that unlike the Update() function GUI components can be called in it.
You can think of OnGUI as a loop. It will call GUI components declared inside of it in order, then do it all over again every frame. So if you hook into this loop and block some components from being called at runtime, the very next frame those componants will not be rendered.
Here is a set of functions that can allow you to do this is a specified time:
private bool guiIsOn = true;
private void TurnOffGUIInSeconds(int seconds)
{
StartCoroutine(_TurnOffGUIInSeconds(seconds));
}
private IEnumerator _TurnOffGUIInSeconds(int seconds)
{
yield return new WaitForSeconds(seconds);
guiIsOn = false;
}
void OnGUI()
{
if(guiIsOn)
{
GUI.Label(new Rect(5,5,5,5), "Label text");
}
}