Recursive spawner instantiating way too many gameobjects - unity3d

I'm trying to make a recursive function that will instantiate a bird at a random interval of time between a min and max value, and then call itself so that the process starts over and a new bird will be instantiated after another random interval. The problem is that currently after the first random interval passes an infinite (close) number of birds are being instantiated all at once. But I don't see where I went wrong. SpawnBird() has been tested and only instantiates one bird when called on its own.
void Start()
{
minBirdWait = 10;
maxBirdWait = 25;
BirdGenerator();
}
void BirdGenerator()
{
float timer = Random.Range(minBirdWait, maxBirdWait);
Invoke("SpawnBird", timer);
BirdGenerator();
}

I ended up turning it into a coroutine and it works now.
IEnumerator BirdGenerator()
{
while(true)
{
float timer = Random.Range(minBirdWait, maxBirdWait);
yield return new WaitForSeconds(timer);
SpawnBird();
}
}

Related

Why does my game freeze inside coroutine?

sorry if this question is very basic. I created a coroutine that detects collisions every 5 seconds. Inside this coroutine I would like to execute an animation for 3 seconds so I came up with this bit of code:
private IEnumerator OnCollisionStay2D(Collision2D collision)
{
if (collision.collider.tag == "mama") {
//interrupt movement
float currentTime = Time.time;
float madBegin = Time.time;
while (currentTime - madBegin < 3)
{
personAnimator.runtimeAnimatorController = Resources.Load("Animations/human1mad_0") as RuntimeAnimatorController;
currentTime = Time.time;
Debug.Log(currentTime);
}
isAngry = true;
}
yield return new WaitForSeconds(5);
}
I logged the elaped time in the while loop and currentTime does not seem to be updating. As a result, the elapsed time equals 0 and my while loop never exits. Furthermore since this is executing in a coroutine that is supposed to run concurrently I don't get why a nonexiting while loop would freeze the entire game.
Clarification is much appreciated.
From the Unity docs on Time.time
The time at the beginning of this frame (Read Only)
This means that your Time.time will not change over the course of your loop so the difference between currentTime and madBegin will always be 0.
Time.realtimeSinceStartup may be what you're looking for.

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 change a sprite to another and then back after 1 second

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();
}
}

Time Delay for a process in Unity 3D

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 :)