Coroutine loop freezes on unity3d run time - unity3d

this is my unityScript code that have a simple coroutine loop. this Code works very well with editor test but on mobile phone and real time test the counter freezes if you want to go to main menu and come back again for counting down.
in other words you start from level 1=>2(every thing is run very well) after that 2=>1 and when you again go to 2 level count down will freeze at first number!!
function Start () {
StartCoroutine("DoSomething");
}
-
function DoSomething () {
for (var i = 5; i >= 0; --i) {
print("Future : \n" + i);
yield WaitForSeconds(1);
print("counting : \n " + i);
}
}
why should this code work at first time and freeze at second time?

You need to return finally the control from your yield, try this:
function DoSomething () {
for (var i = 5; i >= 0; --i) {
print("Future : \n" + i);
yield WaitForSeconds(1);
print("counting : \n " + i);
}
yield return null;
}

Related

How to prevent my method launches every loop 10 seconds earlier

I want my other method launches precice 1:50 (10 seconds before ending) The problem is, it launches 10secondes too early every loop. It looks like
1 st loop
1:50 Method launches - correct
2 nd loop
3:40 Method launches - incorrect (10 seconds shorter)
3 rd loop
5:30 Method launches - incorrect (10 seconds shorter)
4 th loop
7:20 Method launches - incorrect (10 seconds shorter)
......
I want every 110 seconds my method launches precisely every 110 seconds.
the code:
private void OnTimerElapsed(object sender, ElapsedEventArgs e)
{
Application.Current.Dispatcher.BeginInvokeOnMainThread(() =>
{
MainTimer.Text = stopwatch.Elapsed.ToString(#"hh\:mm\:ss");
double counter = stopwatch.Elapsed.TotalSeconds;
Tasklabel.Text = counter.ToString(); // tried to look what's going on
if (((int)counter % 120 == 0 && (int)counter != 0))
{
Value = false;
stopwatch.Stop();
timer.Stop();
// do sth
}
// I tried
//counter2 += 10;
// also tried to assign another variable // double counter2 = stopwatch.Elapsed.TotalSeconds;
if (((int)counter2 % 110 == 0 && (int)counter2 != 0))
{
// do sth
}
});
}
How to write it properly
You could make a small change to the code.
private void OnTimerElapsed(object sender, ElapsedEventArgs e)
{
Application.Current.Dispatcher.BeginInvokeOnMainThread(() =>
{
MainTimer.Text = stopwatch.Elapsed.ToString(#"hh\:mm\:ss");
double counter = stopwatch.Elapsed.TotalSeconds;
if ((((int)counter + 10) % 120 == 0 && (int)counter != 0)) // make changes here
{
//make a stop at 1:50, 3:50, 5:50...
Value = false;
timer.Stop();
stopwatch.Stop();
counter = 0;
// do sth
Console.WriteLine("TIMER Close==============");
}
});
}
Hope it works for you.

how can i synchronize between the screenshot and the extraction of gameObject location in Unity?

I’m trying to create data for detection.
not on android.
I’m moving an object on screen and every X seconds I’m saving a screenshot and the coordinate of the object on a json file.
The problem is that the screenshot doesn’t happening in the exact time I’m asking it to but the coordinate finding and json file creation is.
I managed to solve this problem by waiting a fixed frame number between each of the operation.
public static class WaitFor
{
public static IEnumerator Frames(int frameCount)
{
while (frameCount > 0)
{
frameCount--;
yield return null;
}
}
}
public IEnumerator CoroutineAction()
{
yield return StartCoroutine(WaitFor.Frames(3));
}
public void Save()
{
ScreenCapture.CaptureScreenshot(#".\" + counter + ".png");
CoroutineAction();
Vector3 location = Camera.main.WorldToScreenPoint(gameObject.transform.position);
System.IO.File.WriteAllText(#".\" + counter + ".json", toJson(location));
counter++;
}
This solution somewhat works but it still not the exact location of the nodes and I would appreciate it if someone would know a better and cleaner solution.
Another problem I have is that if I try to use the super-resolution option
Example :
ScreenCapture.CaptureScreenshot(#".\" + counter + ".png" , 4);
And multiply the location vector by 4.
The locations will not be in the location of the object in the 2D image, not even close. Which doesn’t make any sense and It would really help me if I could create a bigger image without the use of external libraries to extend my picture by 4 (which work perfectly fine).
Might not be answering your question so far but what I meant with the comment is
currently you 1. use the wait coroutine wrong and 2. it doesn't do anything
To 1.:
public void Save()
{
ScreenCapture.CaptureScreenshot(#".\" + counter + ".png");
CoroutineAction();
Vector3 location = Camera.main.WorldToScreenPoint(gameObject.transform.position);
System.IO.File.WriteAllText(#".\" + counter + ".json", toJson(location));
counter++;
}
What happens actually is:
ScreenCapture.CaptureScreenshot(#".\" + counter + ".png");
Vector3 location = Camera.main.WorldToScreenPoint(gameObject.transform.position);
System.IO.File.WriteAllText(#".\" + counter + ".json", toJson(location));
counter++;
is executed immediately. Than "parallel" (not really ofcourse) the coroutine would run if you used StartCoroutine(CoroutineAction());
But now to 2.:
the routine
public IEnumerator CoroutineAction()
{
yield return StartCoroutine(WaitFor.Frames(3));
}
does absolutetly nothing than waiting 3 frames ...
I guess what you wanted to do instead is something like
// actually start a coroutine
public void Save()
{
StartCoroutine(SaveRoutine());
}
// optional parameter superSize => if not provided uses 1
private IEnumerator SaveRoutine(int superSize = 1)
{
// makes sure the factor is always >= 1
var sizeFactor = Mathf.Max(1,superSize);
ScreenCapture.CaptureScreenshot(#".\" + counter + ".png", sizeFactor);
//Wait for 3 frames
// I think this makes more sense than your dedicated class with the static method
for (int i = 0; i < 4; i++)
{
yield return null;
}
Vector3 location = Camera.main.WorldToScreenPoint(gameObject.transform.position) * sizeFactor;
System.IO.File.WriteAllText(#".\" + counter + ".json", toJson(location));
counter++;
}
Than as also said already I'm not sure because I can't tell if you should get the location in the frame where you start the capture or in the frame where you end. But I guess you rather should get the location beforehand and than start the capture:
// optional parameter superSize => if not provided uses 1
private IEnumerator SaveRoutine(int superSize = 1)
{
// make sure the factor is always at least 1
var sizeFactor = Mathf.Max(1,superSize);
var location = Camera.main.WorldToScreenPoint(gameObject.transform.position) * sizeFactor;
ScreenCapture.CaptureScreenshot(#".\" + counter + ".png", sizeFactor);
//Wait for 3 frames
// I think this makes more sense than your dedicated class with the static method
for (int i = 0; i < 4; i++)
{
yield return null;
}
System.IO.File.WriteAllText(#".\" + counter + ".json", toJson(location));
counter++;
}
Also note that as mentioned in the linked duplicate
The CaptureScreenshot returns immediately on Android. The screen capture continues in the background. The resulting screen shot is saved in the file system after a few seconds.
You are waiting 3 Frames .. that's usully not even a 1/10 of a second (depending on the frame rate obviously).
You should rather wait for actual seconds using WaitForSeconds e.g.
yield return new WaitForSeconds(3);
to wait e.g. 3 seconds. BUT also note that this will not work in combination with suggested Time.timeScale since WaitForSecodns depends on the timeScale as well.
I think you also should not / don't need to use
#".\" + counter + ".png"
in the filename but simply
counter + ".png"
since
On mobile platforms the filename is appended to the persistent data path. See Application.persistentDataPath for more information.
Try to pause your movements like this:
Time.timeScale = 0;
(Game loop continues)
You can wait some frames/time, or see if you get some feedback when the screenshot is done.
This works best if you use Time.deltaTime in your Update() method which is recommended anyway.

Saving Cubemap to file and load it in Unity

I am trying to save cupemap object in unity and load it to perform a SAVE/LOAD function in my application so I tried this code to save my cubemap and it works fine :
foreach (var gameObj in FindObjectsOfType(typeof(GameObject)) as GameObject[])
{
if (gameObj.name.StartsWith("Chambre"))
{
PlayerPrefs.SetString("Chambre " + (l + 1), gameObj.name);
var chambre = new Material(Shader.Find("Skybox/Cubemap"));
chambre = gameObj.GetComponent<MeshRenderer>().material;
var Texture1 = chambre.GetTexture("_Tex");
if (!AssetDatabase.Contains(Texture1))
{
AssetDatabase.CreateAsset(Texture1, "Assets/Resources/Saved/chambre" + (l + 1) + ".mat");
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
l++;
}
}
But the problem here that I use the import UnityEditor which is not accepted when you try to build the game. So is there another solution to save my cubemap and reload it without using "AssetDatabase.CreateAsset" because it called by UnityEditor?
I don't completely understand what you want to archive.
In a build there are no "Assets" anymore so the methods make no sence in a build.
To use them in the Editor anyway and be able to build your code you just have to use pre-processors #if UNITY_EDITOR <your code> #endif arround code blocks that shall only exist in the Editor:
#if UNITY_EDITOR
using UnityEditor;
#endif
...
#if UNITY_EDITOR
foreach (var gameObj in FindObjectsOfType(typeof(GameObject)) as GameObject[])
{
if (gameObj.name.StartsWith("Chambre"))
{
PlayerPrefs.SetString("Chambre " + (l + 1), gameObj.name);
var chambre = new Material(Shader.Find("Skybox/Cubemap"));
chambre = gameObj.GetComponent<MeshRenderer>().material;
var Texture1 = chambre.GetTexture("_Tex");
if (!AssetDatabase.Contains(Texture1))
{
AssetDatabase.CreateAsset(Texture1, "Assets/Resources/Saved/chambre" + (l + 1) + ".mat");
AssetDatabase.SaveAssets();
AssetDatabase.Refresh();
}
l++;
}
}
#endif
So those codeblocks will not be included/executed in your build.

if Statement Acting Up

This is within the Update function. Excuse the brakeTorque stuff, that's just a bandaid for now. This is a drag racing game, and Staged means ready to go. Once both cars are staged, and the race hasn't started yet, then there should be a delay of 5 seconds then the words "GO 1" should appear (I added stupidCounter as a debugging tool). Then it sets the start time. Then it sets Racing to true in order to keep it from jumping back into this if statement again.
The issue is that it keeps jumping back in the if statement every frame; printing: GO1 GO2 GO3 etc.
The word "GO" is not mentioned anywhere else in any other script.
The "Racing" boolean is not mentioned anywhere else in any script.
Here's my code:
if(Staged && OtherCarStaged() && !Racing)
{
RearRightWheel.brakeTorque = 10000;
RearLeftWheel.brakeTorque = 10000;
FrontRightWheel.brakeTorque = 10000;
FrontLeftWheel.brakeTorque = 10000;
yield WaitForSeconds(5);
stupidCounter += 1;
Debug.Log("GO " + stupidCounter);
mainTimerStart = Time.realtimeSinceStartup;
Racing = true;
}
I'm assuming your function is a coroutine. The issue in your code is probably because you are calling the coroutine in every update frame. You either need to add a check to call the coroutine only once, or use your own timer to handle this instead of a coroutine.
Based on your mentioned requirement I think your code should go like this
var timeLeft : float = 5;
function Update()
{
StartCountdown();
}
function StartCountdown()
{
if(Staged && OtherCarStaged() && !Racing)
{
// your stuff
timeLeft -= Time.deltaTime;
if(timeLeft <= 0)
{
Debug.Log("GO");
mainTimerStart = Time.realtimeSinceStartup;
Racing = true;
}
}
}
Or, if you want to go with Coroutines, it would go like this
function Update()
{
if(!countdownStarted && Staged && OtherCarStaged())
StartCoroutine(StartCountdown(5));
}
var countdownStarted : bool = false;
function StartCountdown(float time)
{
countdownStarted = true;
yield WaitForSeconds(time);
Debug.Log("GO ");
mainTimerStart = Time.realtimeSinceStartup;
Racing = true;
}

Digging technique counter

I'm making and game which has digging as it's feature so I need timer which will count exact float (in seconds) and then destroy gameObject. This is what I tried now but it's freezing unity:
function Update()
{
if (Input.GetMouseButtonDown(0))
{
digTime = 1.5; // in secounds
}
while (!Input.GetMouseButtonUp(0)) // why is this infinite loop?
{
digtime -= Time.deltaTime;
if (digtime <= 0)
{
Destroy(hit.collider.gameObject);
}
}
Here is a basic example how you can check if player has clicked for a certain time period.
#pragma strict
// This can be set in the editor
var DiggingTime = 1.5;
// Time when last digging started
private var diggingStarted = 0.0f;
function Update () {
// On every update were the button is not pressed reset the timer
if (!Input.GetMouseButton(0))
{
diggingStarted = Time.timeSinceLevelLoad;
}
// Check if the DiggingTime has passed from last setting of the timer
if (diggingStarted + DiggingTime < Time.timeSinceLevelLoad)
{
// Do the digging things here
Debug.Log("Digging time passed");
// Reset the timer
diggingStarted = Time.timeSinceLevelLoad;
}
}
It is firing every DiggingTime of seconds even the player is holding the button down. If you want that the player needs to release the button and press again one solution is to add Boolean telling if the timer is on or not. It can be set true on GetMouseButtonDown and false on GetMouseButtonUp.
Update function is called every frame. If you add a while loop inside this function waiting for the mouseButtonUp, you'll freeze Unity for sure.
You don't need the while loop. Just check GetMouseButtonUp without while loop.
EDIT
This is the Update function:
void Update ()
{
if ( Input.GetMouseButtonDown( 0 ) )
{
digTime = 1.5f;
}
else if ( Input.GetMouseButton( 0 ) )
{
if ( digTime <= 0 )
{
Destroy( hit.collider.gameObject );
}
else
{
digTime -= Time.deltaTime;
}
}
}
Minor controls should be added to avoid destroying gameObject several times, but this is the idea to proceed