Search code examples
timetimerwaitmit-scratch

scratch timer method not accurate


So I'm making a game that depends greatly on time and how fast a person responds and I noticed a little bug where it has a randomized time. For example: I have the method

wait pick random .01 to 3 seconds set ghost effect to 0 reset timer repeat 5 wait .05 seconds change ghost effect by 20

and each time I run this, I get different times. It can't be the fact that I'm randomizing the wait time because the reset timer method is after that block. I ran some tests and I concluded that 7 out of the 12 times i got 0.8 seconds, which is what I'm trying to get. 3 out of the 12 times, I got 0.7, and 2 out of the 12 times I got 0.6. If there is any way to make the timer more accurate or improve my code to reduce lag, it would be much appreciated.


Solution

  • One general solution would be to make how ghosted the sprite is be a "function" based on how long since the animation started. This would work something like this:

    When space key pressed, set (animation start) to timer. Repeat until (timer - animation start) > 4: set color effect to (200 / 4) * (timer - animation start).

    This animation animates the sprite changing all the way around the color effect wheel across four seconds. It'll be most clear how that works if we go over it step by step.

    We want our animation to last for four seconds. But how do we actually make it last that long? As was seen in your script, just using a "repeat (40): wait 0.1 seconds" doesn't always result in waiting exactly four seconds. Instead, we use a "repeat until" loop: "repeat until (timer - animation start) > 4".

    We get the "animation start" variable just by setting it to the current timer value when we start the animation. We'll see "timer - animation start" again later in; what it really means is the progress through the animation: "timer - animation start" starts at zero and gradually increases to four as the animation runs. (Of course, when it reaches 4, we want to stop the animation, and that's why we use the "repeat until" block.)

    Here's the big question: how, given the current amount of time through the animation, can we decide what the color effect should be? It turns out, that's not so hard to answer, but we do need to think through it since it takes math. We want to transition from 0 to 200 over a period of 4 seconds. You can write that down as a rate: 200 units per 4 seconds, so, 200 / 4. Then we just multiply that rate by how far through the animation we are: (200 / 4 * progress). Progress is easy to get, again; we just reuse the "timer - animation start" blocks.

    Do you believe that I'm right? Here's a list with some numbers to convince you (but really, you should try out this script yourself!):

    • 0s: (timer - animation start) = 0, color effect = (200/4) * 0 = 0. This is the starting point of the animation, so it makes sense for the color effect to be zero.
    • 1s: (timer - animation start) = 1, color effect = (200/4) * 1 = 50.
    • 2s: (timer - animation start) = 1, color effect = (200/4) * 2 = 100. Half way through the 4-second long animation: since we're transitioning from 0 to 200, it makes sense to now be at 100.
    • 3.5s: (timer - animation start) = 3.5, color effect = (200/4) * 3.5 = 175.
    • 4s: (timer - animation start) = 4, color effect = (200/4) * 4 = 200. Now that we're at the end, we've transitioned all the way to 200.

    To try this yourself, I do recommend implementing some "artificial lag". That just means adding a "wait (random 0.1 - 0.3) seconds" block to simulate the lag that might show up in a very complex project or on a slow computer.

    Since we're just dealing with a basic math formula, it's very easy to change the numbers to get a different result. Here is a script which transitions from 0 to 100 over 2 seconds:

    When G key pressed, set animation start to timer. Repeat until (timer - animation start) > 2: set ghost effect to (100 / 2) * (timer - animation start).

    But here's a point where you might find a "gotcha" -- look at what happens if you add artificial lag:

    The cat turns ghost-y.. but doesn't ENTIRELY disappear!

    The cat turns ghost-y.. but doesn't ENTIRELY disappear! Yikes! So, what caused this?

    Here's the problem: The animation stops before (timer - animation start) is exactly 2 seconds. So, we never run that 2s step, where the ghost effect would be 100 - and we're left with a sprite that is not entirely ghosted.

    Luckily, the solution is simple. Just make sure you additionally switch to the final state after the animation has ended. Of course, that just means attaching the appropriate "set effect" block right after the "repeat until" loop:

    When G key pressed, set (animation start) to timer. Repeat until (timer - animation start) > 2: set ghost effect to (100 / 2) * (timer - animation start). After the loop, set ghost effect to 100.

    Now the sprite's ghost effect will be set to 100 immediately after the loop, regardless of whatever it ended with.


    By the way, when you tested these scripts out for yourself - you did, didn't you? - did you notice that this animates very smoothly? In fact, it animates as smoothly as possible. It will always animate at the top framerate that the user's computer can handle, since it runs every Scratch frame (there is no "wait n seconds" block)! Also, a simple test of your understanding - how can you re-implement the "glide (n) seconds to (x) (y)" block using this? It's definitely possible!