1
\$\begingroup\$

I'm trying to make an element (3D object or UI element) blink in such way:

  1. There is a timer which counts down 10.0f seconds to 0.0f.
  2. In 10.0f to 9.9f range the object should fade out from opacity 1 to 0.
  3. In 9.9f to 9.8f range the object should fade back in from 0 to 1.
  4. Then wait 300ms.
  5. Then repeat and finally fade out and disappear completely on last 1.0f to 0.0f.

I created a function in Unity:

void Update(){
    if ( myTimeVariable < 10.0f && myTimeVariable > 9.9f ) {
        gameObject.GetComponent<Renderer>().material.color.a = (10.0f - myTimeVariable);
    } else if() {
        // The logic continues and makes a long set of
        // else if statements to cover all times.
    }
}

(It's originally a coroutine that executes after each Update() but here it's shown otherwise for simplicity reasons.)

The problem I'm facing is that at 60 FPS I have these frames available:

0ms 16ms 33ms 50ms 66ms 83ms 100ms

And on 30 FPS I may have only these:

0ms 33ms 66ms 100ms

On 30 FPS opacity is changing this way: First I get fully opaque object on 0ms frame. Then on 33ms frame I get 0.67 opaque object. Then on 66ms frame I get 0.34 opaque object and frame 100ms doesn't belong to "fade out" if statement but fading in starts. Object starts to fade in from 0.34 to 1 over the next 100ms even if it never had 0 opacity state.

It seems that my method of making blinking object is flawed. It never reaches 0 opacity and it's very visible in the game that the last state was 0.34 opacity and not 0. Is there a way to make that blinking be certain to go to 0 opacity regardless of frame rate?

Edit: For "fade in" I only call Unity's CrossFadeAlpha() once. That's why it starts from 0.34 and goes to 1 but even if I changed that to the other method still my frames may be available at 80ms and then 120ms and opacity 0.2 -> 0.2 is the result (never desired 0).

\$\endgroup\$
  • \$\begingroup\$ are you sing Time.DeltaTime to measure your time ? You might want to consider a fps independent timer. \$\endgroup\$ – Uri Popov Apr 27 '16 at 10:07
  • \$\begingroup\$ I'm using Time.time to prepare myTimeVariable. 10.0f is 10 seconds regardless of FPS. \$\endgroup\$ – fyvaydmx Apr 27 '16 at 11:04
1
\$\begingroup\$

Update is called once a frame, this means that the time between calls depends directly on the frame-rate. This problem can be solved by defining the alpha, a, as a function of t, where 0 \leq t \leq 10. At t = 10, a = 1, and at t = 0, a = 0, so we can define our function as f(t) = 0.1 * t. Psuedo code could appear as follows.

// initialize t=0
Update:
  t += Time.deltaTime
  if ( inRange(t, 0, 10) )
    setAlpha(0.1 * t)
  else
    setAlpha(0)

In Unity, Time.deltaTime expresses how much time in seconds has elapsed since the last frame.

\$\endgroup\$
0
\$\begingroup\$

For stylish control:

Define this 50-length array A of numbers:

1,0.9,0.8,0.7,0.6,0.5,0.4,0.3,0.2,0.1,0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1

These are alpha values for 1/2 second, which is the repeat sequence for the 9 s blinking part of your question (0.1 + 0.1 + 0.3 s). These 9 seconds hold 18 repetitions of 0.5 seconds each. You only specify the 0.5 seconds with this array. As you can see, the values begin with 1, 10 slots later (= 0.1 s later) you have 0, then back to 1, where it stays for 30 numbers (0.3 s). After this, it can repeat.

Whenever you visit your function, you have a time T handy (as you stated).

Change your T value to start from 0 and end at 10. Ie. you have values of T like:

0, 0.33, 0.66, ... 5.99, 6.32, ... [any]

Use the value of T to index into the array A of 50 elements, and pick your alpha there. Index using this statement (pseudo code):

IND = 1 + modulo(floor(100 * T), 50)

This will produce a number between 1 and 50, no matter what T is. If for example T=0, IND will be 1. If T=0.05, IND will be 6. If T=7.142857, IND will be 15. If T=9, IND will be 1. Etc. Remove the "1 +" if your language uses zero-origin.

Your functions would look like this (sorry, i don't know the expressions for modulo and floor, ie. round down, in your language):

A = (array of) 1,0.9,0.8,0.7,0.6,0.5,0.4,0.3,0.2,0.1,0,0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1
B = (array of) 0.9,0.8,0.7,0.6,0.5,0.4,0.3,0.2,0.1,0 // For the last fade out (length 1 second)

and in the loop:

:If (T > 10)
    [exit this thingy, do housekeeping]
:else
    :If (T < 9)
        IND = 1 + modulo(floor(100 * T), 50) // modulo(dividend, divisor)
        alpha = A[IND]
    :Else
        IND = 1 + modulo(floor(10 * T), 10) // could simplify into alpha = 10 - T
        alpha = B[IND]
    :End
    [set alpha to your object]
:end

Of course, you can now make different sequences, change the lenght of them, make other effects, etc.

\$\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.