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;
}
Related
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
I am new to unity and I am creating my first ever endless runner game. I currently have a cube as a player and another cube (prefab) as the track. I have created a script which aims at instantiating the prefab at runtime and moving the track towards the player (instead of moving the player). For some reason, the tracks instantiated at runtime are being generated at the same position and don't seem to move towards the player. Could anyone tell me if I am doing it right please? How do I generate an infinite/endless track? Thanks in advance
public GameObject[] trackPieces;
public float trackLength;
public float trackSpeed;
private GameObject trackObject;
// Update is called once per frame
void Update ()
{
GenerateInfiniteTrack();
}
private void GenerateInfiniteTrack()
{
trackObject = InstantiatePrefab();
trackObject.transform.Translate(-Vector3.forward * Time.deltaTime * trackSpeed);
}
private GameObject InstantiatePrefab()
{
return Instantiate(trackPieces[Random.Range(0, trackPieces.Length)]) as GameObject;
}
trackObject is overwritten every frame
Your track is not moving because the call to move it occurs only once at instantiation and never again. The value of which track to move is overwritten every update(). Here is what happens:
Every update you make a call to:
private void GenerateInfiniteTrack()
When this method is called you instantiate a new prefab (new instance of your object) and tell the object to move via
trackObject.transform.Translate(-Vector3.forward * Time.deltaTime * trackSpeed);
When you call the method the next time you lose the reference to the original object because you are creating another one that replaces the value held in trackObject. So the previously generated object will never move again. Your new object moves once and then is lost, again, and repeat.
Below I have suggested some code that may work. You will still have to decide how you will dispose of the track objects since you are creating them at a very fast rate. If you can think of a way to use InvokeRepeating() to address this problem, then that would be the cleanest. Invoke repeating has some limitations, like not being able to pass parameters to your repeating function so I did not use it here, but it is conceivably an alternative.
Here is my example.
var TrackObjects = new List<GameObject>();
private void MoveInfiniteTrack()
{
foreach(GameObject gO in TrackObjects )
{
gO.transform.Translate(-Vector3.forward * Time.deltaTime * trackSpeed);
}
}
private void GenerateInfiniteTrack()
{
TrackObject = InstantiatePrefab();
TrackObjects.Add(TrackObject);
}
void Update ()
{
GenerateInfiniteTrack();
MoveInfiniteTrack()
}
void Foo()
{
// Delete your track on some kind schedule or you will quickly get infinite objects and massive lag.
}
As a general observation, creating track this much (60x a second) is very inefficient. I would suggest that you either:
A. Create much larger segments of track, for example once every 1 or 10 seconds and move it along. I don't remember exactly how to do this, but it went something like this:
var lastchecked= datetime.now
Update()
{
if((datetime.now - lastchecked).seconds > 1)
{
// put here what you will do every 1 second.
lastchecked= datetime.now
}
}
B. Create a large cylinder and rotate it to use as a track.
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.
I'm developing a simple game in Unity2D, in which several monsters eat things that are dragged to them. If the right object is dragged to the monster, the score goes up by one and the monster should make a happy face, otherwise, score goes down and makes a sad face. This is the code I'm using for that (minus the transitions to happy/sad):
if (transform.name.Equals ("yellow")){
if (collinfo.name.Equals ("plastic(Clone)")) {
Debug.Log ("+1");
audio.Play ();
GameSetup.playerScore += 1;
gs.GetComponent<GameSetup>().removeit(aux);
}
else {
Debug.Log ("-1");
audio.Play ();
if (GameSetup.playerScore == 0)
{}
else
{
GameSetup.playerScore -= 1;
}
gs.GetComponent<GameSetup>().removeit(aux);
}
The audio played is just a 'munching' sound.
I want the monster to change sprites to happyFace (via GameObject.GetComponent ().sprite = happyFace), wait one second and then switch back to it's normal sprite, but don't know how to implement that waiting period.
Any and all help would be much appreciated.
This can be implemented several ways, however, I would use a method that returns an IEnumerator like so…
This assumes you have a variable in your script that has a reference to the SpriteRenderer that is attached to the GameObject with this script.
SpriteRenderer sr = GetComponent <SpriteRenderer> ();
I also assume you have an original sprite and the possible sprites to change to as variables too.
public Sprite originalSprite;
public Sprite happyFaceSprite;
public Sprite sadFaceSprite;
public IEnumerator ChangeFace (Sprite changeToSprite)
{
sr.sprite = changeToSprite;
yield return new WaitForSeconds (1.0f);
sr.sprite = originalFaceSprite;
}
You would then call this function with the applicable sprite as the variable.
if (happy)
StartCoroutine (ChangeFace (happyFaceSprite);
else
StartCoroutine (ChangeFace (sadFaceSprite);
Because the ChangeFace method returns an IEnumerator, we must call that function with the StartCoroutine function. The method will run until it reaches the yield return new WaitForSeconds (1.0f) function, then wait for 1.0f seconds and then resume where it stopped last time.
Understand?
Note
I haven't tested this but I don't see why it wouldn't work.
Put a floating point variable in your monster controller, call it happyTimer or something. It should start at zero.
Then in your Update function you should check happyTimer is above zero. If it is, then subtract Time.deltaTime and check again. If happyTimer is zero or below for the second check, then call your function that resets the sprite.
When you set the sprite to "happy face", also set happyTimer = 1. This will begin the countdown starting with the next Update call.
The relevant part of Update would look like this:
if(happyTimer > 0) {
happyTimer -= Time.deltaTime;
if(happyTimer <= 0) {
resetSprite();
}
}
I have to give the delay for the process to happen, which I am calling in the Update function.
I have tried CoUpdate workaround also. Here is my code:-
function Start()
{
StartCoroutine("CoStart");
}
function CoStart() : IEnumerator
{
while(true)
{
yield CoUpdate();
}
}
function CoUpdate()
{
//I have placed the code of the Update().
//And called the wait function wherever needed.
}
function wait()
{
checkOnce=1; //Whenever the character is moved.
yield WaitForSeconds(2); //Delay of 2 seconds.
}
I have to move an object when a third person controller(which is another object) moves out of a boundary. I have included "yield" in my code. But, the problem happening is: The object which was moving when I gave the code for in the Update(), is moving, but isn't stopping. And it is moving up and down. I don't know what is happening! Can someone help? Please, thanks.
I am not entirely clear what you are trying to accomplish, but I can show you how to set up a Time Delay for a coroutine. For this example lets work with a simple cool down, much like you set up in your example. Assuming you want to continuously do something every 2 seconds while your game is running a slight modification can be made to your code.
function Start()
{
StartCoroutine(CoStart);
}
function CoStart() : IEnumerator
{
while(true)
{
//.. place your logic here
// function will sleep for two seconds before starting this loop again
yield WaitForSeconds(2);
}
}
You can also calculate a wait time using some other logic
function Start()
{
StartCoroutine(CoStart);
}
function CoStart() : IEnumerator
{
while(true)
{
//.. place your logic here
// function will sleep for two seconds before starting this loop again
yield WaitForSeconds(CalculateWait());
}
}
function CalculateWait() : float
{
// use some logic here to determine the amount of time to wait for the
// next CoStart cycle to start
return someFloat;
}
If I have missed the mark entirely then please update the question with a more detail about what you are attempting to accomplish.
I am not 100% sure that I understand you question but if you want to start one object to move when the other is out of bound then just make a reference in the first object to the second object and when the first object is out of bounds (check for this in Update of the first object) call some public function StartMove on the second object.
I wouldn't suggest CoRoutines. It can sometimes crash your computer. Just define a variable and decrement it. Example:
private float seconds = 5;
then do anywhere you want to delay:
seconds -= 1 * Time.deltaTime;
if(seconds <= 0) {your code to run}
This will make a delay of 5 seconds. You can change 5 to any value to change the number of seconds. Also you can speed up the decrement by changing the value of 1. (This is mostly useful when you want to sync 2 delayed actions, by using the same variable)
Hope this helps. Happy coding :)