I had a problem in my program, and at first I thought it was related to coroutines. Every script that featured a coroutine worked slower in it's entirety. I later discovered this was the case because of a difference in FPS. In the editor, the FPS was 800, while it was 60 in the build. When I changed the code, so it would be 800 in both, everything worked fine.
This fixed everything, but this feels like a weird solution. It's a very simple project, so I don't mind for now, but I'd still like to know what's going on. Any ideas?
public IEnumerator BeInvincible()
{
isInvincible = true;
for (float i = 0; i < invistime; i += invistimedelta)
{
// Alternate between 0 and 1 scale to simulate flashing
if (sprite.color == new Color(1, 1, 1, 1))
{
sprite.color = new Color(1, 0, 0, 1);
}
else
{
sprite.color = new Color(1, 1, 1, 1);
}
yield return new WaitForSecondsRealtime(invistimedelta);
}
sprite.color = new Color(1, 1, 1, 1);
isInvincible = false;
}
I use fixedupdate for movement, and update for inputs. When I put inputs in fixedupdate (50 FPS), the game reacts the bad way, even when the game runs in base 800 fps.
Bad in what way?
In any case, if there is a problem at the time of code execution
Or a direct delay in performance, most likely the problem is from here
yield return new WaitForSecondsRealtime(invistimedelta);
The execution of a coroutine can be paused at any point using the yield statement. When a yield statement is used, the coroutine pauses execution and automatically resumes at the next frame. See the Coroutines documentation for more details.
And also you can review this
Related
Help me understand what the problem is in Unity3D.
I generate an infinite number of blocks in motion.
But the higher the speed of movement, the larger the gap between objects. As far as I understand, the gap will be sampled due to the frame refresh time.
How to generate objects close to each other?
void Update()
{
if (startspawn)
{
spawn1 = Instantiate(spawner);
spawn1.GetComponent<Rigidbody>().velocity = new Vector3(15, 0, 0);
startspawn = false;
}
if (spawn1.transform.position.x - startcoordinateX > size)
{
spawn2 = Instantiate(spawner);
spawn2.GetComponent<Rigidbody>().velocity = new Vector3(15, 0, 0);
spawn1 = spawn2;
}
}
Screenshot
Use FixedUpdate instead of Update. FixedUpdate ensures the time between calls is the same. You can read more about it here: https://docs.unity3d.com/ScriptReference/MonoBehaviour.FixedUpdate.html.
I'll leave it here.
Managed to solve the problem by simple binding to Time.deltaTime.
Now, even at high speed, there are no gaps.
Just use.
spawn1.GetComponent().velocity = new Vector3(speed * Time.deltaTime, 0, 0);
You can use Fixed update which makes the time between calls the same.
This is just a portion of my code, and I hope will be easy enough to understand. I found a way to "fix" it, but I still don't understand this:
I set my float countDownTime to 2f. In DisplayTime(), i thought the do-while loop would count from 2 down to 0, but instead starts counting down from 0 to negative numbers. I thought that it would count down and stop when countDownTime reaches 0, as assigned in the while(countDownTime >= 0) but it continues beyond that. Thanks for any feedback or assistance.
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Space) && !hasStarted)
{
hasStarted = true;
StartGame();
}
DisplayTime();
}
void DisplayTime()
{
if (timerStart)
{
do
{
countDownTime -= Time.deltaTime;
} while (countDownTime >= 0);
timer2.text = Math.Round(countDownTime, 2).ToString();
}
}
I made changes to it, which is my fix:
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.Space) && !hasStarted)
{
hasStarted = true;
StartGame();
}
DisplayTime();
}
void DisplayTime()
{
if (timerStart && countDownTime >= 0)
{
do
{
countDownTime -= Time.deltaTime;
} while (countDownTime >= 2);
timer2.text = Math.Round(countDownTime, 2).ToString();
}
}
First off, I don't see where you're initially setting countDownTime to 2.
Second, countDownTime will more likely than not stop on a negative number rather than zero. This is because deltaTime is a measure of the ms that have passed since the last update, and varies quite a bit. So you should round it back up 0 if you don't want to be shown as negative.
Finally, I think you want to execute this code as a Coroutine, otherwise the entire loop will execute in a single frame, which is most likely not what you want.
IEnumerator DisplayTime() {
countDownTime = 2;
while (countDownTime > 0) {
countDownTime -= Time.deltaTime;
timer2.text = Math.Round(countDownTime, 2).ToString();
yield return new WaitForEndOfFrame();
}
}
void Start() {
// start coroutine
StartCoroutine(DisplayTime);
}
If you did not want a coroutine, and wanted to called it each frame from your Update method, you can simply drop the while loop.
void DisplayTime() {
if (timerStart) {
if (countDownTime > 0) {
countDownTime -= Time.deltaTime;
}
}
timer2.text = Math.Round(countDownTime, 2).ToString();
}
The benefit of the Coroutine method is it usually easier to manage. Rather than calling a method every time from Update, you can call it once and just let it run. Since the looping is confined to the method itself you can use DisplayTime to set the initial value of countDownTime to 2.
You need to consider 2 factors.
Update() Method is called on every frame. You are executing do-while loop in DisplayTime() which is called from Update() so do while loop will execute many time (once per frame).
do-while loop works differently from normal while loop. do-while loop first executes code inside do{} and only after checks condition and breaks loop if condition is false. so (even if condition is false) code inside do{} will run at least once once. In case of normal while if condition is false code inside loop will not run.
So in your case on first frame countDownTime is subscribed until it will become be less than 0. (So on screen you immediately see 0 instead of 2, because after first frame its
already 0).
after 1st frame on each frame when do-while is executed code inside do is executed only once and than loop immediately breaks because condition countDownTime >= is already false. So after 1st frame countDownTime -= Time.deltaTime;this code gets executed once per frame (without looping multiple times in do-while), that is why it works "correctly" afterwards.
I'm trying to animate Time.timescale using an easing function with DOTween.
Tweening the value itself seems to work:
DG.Tweening.DOTween.To(value => Time.timeScale = value, 1, 0, 0.4f);
But all the SetX() methods mentioned in the documentation are not available on the object this returns and while I found out that I could set the timeScale property, there doesn't seem to be any way to use an easing function for this tween.
What am I missing?
Figured it out:
Unfortunately it seems that all SetX() methods in DOTween are implemented as extension methods. This means that these methods are simply not available if you are trying to use DOTween by specifying the full type, rather than including the namespace.
This works:
using DG.Tweening;
///
DOTween.To(()=> Time.timeScale, x=> Time.timeScale = x, 2, 0.4f).SetEase(Ease.InQuad).SetUpdate(true);
This does not work:
DG.Tweening.DOTween.To(()=> Time.timeScale, x=> Time.timeScale = x, 2, 0.4f).SetEase(DG.Tweening.Ease.InQuad).SetUpdate(true);
It looks to me like you're wanting to pause execution by slowing time down gradually rather than instantaneously stopping it and you want to use an easing method.
To set an ease on a DOTween just add on the Ease method call via chaining like this:
DG.Tweening.DOTween.To(value => Time.timeScale = value, 1, 0, 0.4f).SetEase(Ease.InCubic));
Chain more methods together like this, this runs Debug.Log on every Update call:
DG.Tweening.DOTween.To(value => Time.timeScale = value, 1, 0, 0.4f).OnUpdate(()=>{ Debug.Log(Time.timeScale); }).SetEase(Ease.InCubic);
Running this code shows that Time.timeScale doesn't actually get to zero, probably due to floating point precision errors; however, using the OnUpdate chain method Time.timeScale can be assigned zero once it gets close enough:
DG.Tweening.DOTween.To(value => Time.timeScale = value, 1, 0, 0.4f).OnUpdate(()=>{ if (Time.timeScale < 0.001f) Time.timeScale = 0; }).SetEase(Ease.InCubic);
I hope this helps!
I just can't seem to get while loops to work for me inside Unity. No matter how simple, Unity always freezes on me.
function LoadingLevel (level : int) {
yield;
//progressBar.transform.localScale = Vector3(loadingProgress, 0, 0);
async = Application.LoadLevelAsync(1);
while (!async.isDone) {
loadingProgress = parseInt(async.progress * 100);
}
//Application.LoadLevel(level);
}
This is what I'm currently having trouble with: it compiles, but freezes at runtime. What am I doing wrong?
You are failing to understand how async stuff works in Unity.
Your while loop needs to yield. This is because Unity programs are single-threaded, and yield is how you give time to other "coroutines".
while (!async.isDone)
{
loadingProgress = parseInt(async.progress * 100);
yield;
}
Here are the Unity docs on how this works:
http://docs.unity3d.com/Manual/Coroutines.html
I can't understand why.
I already read lots of documents about it and there are lots of comments that
StopCoroutine(string) can only stop the coroutine starts with StartCoroutine(string)
the code is below.
In Class CharTouchControl.cpp
if( Input.GetKeyDown( KeyCode.Q ) )
{
_CharControl.StartCoroutine("useFever", 10);
}
_CharControl is a member of CharTouchControl and of course _CharControl is referencing the CharControl instance below.
And InClass CharControl
public IEnumerator useFever(float _duration = 10)
{
if( m_nUseFever < _nFeverMax )
{
Debug.Log("Stop!!");
StopCoroutine("useFever");
yield return new WaitForEndOfFrame();
}
m_fgCharState = CharState._FEVER;
// fever particle
m_psFeverPaticle.Simulate(4);
yield return new WaitForEndOfFrame();
} // end of useFever
When m_nUseFever < _nFeverMax is true, i can see "Stop!!" log,
but the coroutine don't stop and simulate paticles.
anybody help for this?
Just as Jerdak mentioned, you should use yield break when your inside the Coroutine.
the coroutine will automatically stop when it finishes its code block. Using yield break, should send it to the end of the code block.
Additionally you could modify your code so that it only runs the code block if the condition is true. Ive done this in the example below. If the condition is false the coroutine will just wait for the end of the frame and then exit.
public IEnumerator useFever()
{
if( m_nUseFever > _nFeverMax )
{
m_fgCharState = CharState._FEVER;
// fever particle
m_psFeverPaticle.Simulate(4);
}
yield return new WaitForEndOfFrame();
} //Coroutine automatically exits here
EDIT:
Dan Puzey brought up a good point, Are you sure shouldn't be using
Invoke("useFever",10) or InvokeRepeating ("useFever",10,1)
instead? From your code it looks like you just want to run the useFever function in 10 seconds. If thats the case, you should be invoking it instead of running it as a coroutine, and useFever should be a normal function.
Just for reference, there is also CancelInvoke("useFever")