0
\$\begingroup\$

i wanna run a code for my special power for five seconds only and then return to my normal working. when that power activates i am changing my player's material and i wanna return to my old material after those five secs are over. this is the code i tried:

void Start () {

        transform.GetComponent<Renderer> ().material.mainTexture = texts [0];
    }

    void Update () {

        if (condition) {
            for (int t1 = 0; t1 < 5; t1 += (int)Time.deltaTime) {
                transform.GetComponent<Renderer> ().material = mats [0];
            }
            transform.GetComponent<Renderer> ().material.mainTexture = texts [0];
        }
    }

this code breaks the game i think because unity is freezing when the condition is being met.

\$\endgroup\$
  • 1
    \$\begingroup\$ Why are you casting deltaTime to int? That is locking your for loop endlessly, because deltaTime is usually around 16.67ms, which makes it inhibit the value 0.1667f which when cast to int is rounded and due to it being < 0.5, equals 0. You can imagine that 0 + 0 will never reach >= 5, therefore never exit your loop and therefore, lock Unity. \$\endgroup\$ – joltmode Dec 28 '16 at 17:57
  • \$\begingroup\$ What does this question have to do with recursion? \$\endgroup\$ – Krythic Dec 28 '16 at 18:56
7
\$\begingroup\$

Yes, this code freezes the game, because it is an infinite loop. You seem to have two misconceptions which seem to have lead you to writing this code:

  1. Unity processes each Update-method in an independent threat of execution. That's wrong! While an Update method is being executed, nothing else happens in the engine. So it is important that your Update methods (as well as any other message handlers) return quickly.
  2. When an Update method takes a while, Time.deltaTime increases. Also wrong! It gives you the time in seconds it took to complete the last frame, so it will only change between Update's.

Casting Time.deltaTime to int will round it down. Time.deltaTime only changes between Update's and will almost always be far lower than 1.0. that means 0 gets added to t1 over and over again. This for-loop will loop forever and not let Unity do anything else. If you want Unity to be able to resume the normal gameplay while the special power is active, you need to return the program's execution flow back to the engine by leaving your Update method.

There are several patterns you could use to implement this.

Temporary component

You could implement your special effect as a Component which you add to the gameObject when the special effect is activated. Program the component to automatically remove itself when the effect time is over. You can do that by taking the time it was created and having the update-method check if the time has elapsed.

public class ActiveSpecialPower : MonoBehaviour {

    private float startTime;
    private static readonly float POWER_DURATION_IN_SECONDS = 5.0f; 

    void Start() {
          startTime = Time.time;
          // code which happens on activation of the special power
    }

    void Update () {
        if (Time.time > startTime + POWER_DURATION_IN_SECONDS) {
             // code which happens on deactivation of special power
             Destroy(this);
        }
    }
}

Code for activating the special power:

gameObject.AddComponent<ActiveSpecialPower>();

Code for checking if special power is currently active:

if(gameObject.GetComponent<ActiveSpecialPower>() != null) {

Permanent Component

Or you could have your SpecialPower component permanently on your player game object and have a method to activate it:

public class SpecialPower : MonoBehaviour {

    public float remainingTime;
    private static readonly float POWER_DURATION_IN_SECONDS = 5.0f; 


    public void Activate() {
          // code which happens on activation of the special power
          remainingTime = POWER_DURATION_IN_SECONDS;
    }

    void Update () {
        if (remainingTime > 0.0f) {
           // code which happens every update while the power is active
           remainingTime -= Time.deltaTime;
           if (remainingTime <= 0.0f) {
                // code which happens on deactivation of special power
           }
        }
    }
}

Code for activating the special power:

gameObject.GetComponent<SpecialPower>().Activate();

Code for checking if special power is currently active:

if(gameObject.GetComponent<SpecialPower>().remainingTime > 0.0f) {

Coroutine

Another option is to implement your special effect as a Coroutine. A coroutine can use the construct yield return WaitForSeconds(n) to return the program flow back to the engine and continue it after the given time has passed. Such a coroutine-method could look like this:

private IEnumerator ActivateSpecialPowerCoroutine() {
    // code which happens on start of special power
    yield return new WaitForSeconds(POWER_DURATION_IN_SECONDS);
    // code which happens on end of special power
}

Code for activating the special power:

StartCoroutine(ActivateSpecialPowerCoroutine());

Unfortunately there is no good way to check if a coroutine is currently being executed.

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.