jmoenig / Snap

a visual programming language inspired by Scratch
http://snap.berkeley.edu
GNU Affero General Public License v3.0
1.5k stars 744 forks source link

BUG: Adding 'warp' blocks causes pen draw flickering #786

Closed deader69 closed 5 years ago

deader69 commented 9 years ago

http://snap.berkeley.edu/snapsource/snap.html#present:Username=deader-tcssm&ProjectName=body_BUG_redraw_warp_flicker

This character's limbs flicker. They're drawn with pen commands, inside the 'redraw' sprite.

The flicker goes away if you remove the 'warp' blocks, which I (mis)used for clarity, to divide the script into four sections, one for each limb.

Jens, you'll recognize this is the same project as before, which you fixed with a 'redraw' method defined inside each sprite of each limb. That solution worked great, but I decided using 'ringify' and 'run' was too complex for my 9 year old beginners... and I realized that I no longer needed separate Sprite objects for each limb anyway, since we replaced their bitmaps with pen commands.

So I consolidated all limb drawing to one point inside the 'redraw' sprite. Much simpler for my students to understand. It works fine, except when I put those 'warp' statements in.

If you NEST the warps, it'll also be fine, flicker-free. Does 'warp' somehow do an implicit 'wait'? I don't understand why it would cause flickering, I would think it'd be semantically equivalent.

jmoenig commented 9 years ago

Sigh.

Those warp blocks actually slow down redrawing, because they force Snap to redraw after every warp invocation, whereas if you just leave them out Snap would not. As far as I can tell not a single of those warp blocks has any positive effect, certainly not a single one of them speeds up anything. You can safely take them all out.

Also, stuff like wait 0.1 secs doesn't do anything helpful at all, you can take those blocks out.

In fact, if you take out all of those "optimizations" your project works just as you probably want it to work (flicker-free redraw). Check it out:

http://snap.berkeley.edu/snapsource/snap.html#present:Username=jens&ProjectName=body_BUG_redraw_no_warp

I totally understand about not wanting to use rings with beginners. The great thing is you don't have to. Instead you yourself can use rings to make a new control structure for your kids to use, let's call it tell:

tell_block

this block makes another sprite named name do whatever is inside the C-shaped slot.

here's how to make it (see, you can use rings, your kids don't have to):

tell

This block lets you write "redraw" invokations in a single pass, like this:

redraw

And it's very easy to use. You can make this block, export it as a blocks-module, and share it with your kids.

But, see, you don't even have to do this, because your project simply works as intended if you don't try to optimize it :-)

deader69 commented 9 years ago

Yes, I was only using the warp blocks to break a long sequence into four groups, one for each limb. I wasn't trying to optimize, just 'chunk' things together visually. I just didn't expect a negative side effect, that's all. I shall refrain from abusing the warp blocks in the future!

deader69 commented 9 years ago

As for the 'wait milliseconds' code... that's not an optimization attempt. Just the opposite: I'm trying to make the code run at a consistent rate across all platforms. I want it to run SLOWER on fast machines, so that I get a (nearly) consistent rate of motion.

Snap! runs significantly slower on my laptop than it does on our ChromeBoxes. So what's fun and playable on my machine is too fast and crazy for others. It's unplayable on faster machines.

One way to solve that problem is to write everything as a function of 'T', time, like a physics equation. Everything moves relative to some delta-T between frames. Thus the physical 'simulation' is completely independent of the rendering frame rate.

That's the proper, academic solution... but it's asking a lot of kids to understand and code that way.

Another solution is to run at a fixed, known frame rate. That's the way old console and arcade games were coded, knowing that the monitor refreshes at 30hz or 60hz, you just try to get everything done before the next Vsync. If not, everything starts moving at half-speed, very noticeably.

It's a really difficult problem, pros and cons to each approach. I'm not sure the best way to program this in Snap!... I'm trying to slow the 'forever' loops down to running a (nearly) consistent X frames per second, by adding a wait at the bottom.

Otherwise... I'll have to make every argument to every 'move' command be a function of 'current milliseconds'. Internally, the high-level Snap! blocks like 'glide to over seconds' are doing just that.

Anyway, for the yoyo_ants game, I can consider "wait_milliseconds' to be just a global 'speed' parameter, that changes from level to level.

Hope that all makes sense! I'm still learning this 'simple' language. :-)

deader69 commented 9 years ago

Jens, I don't mean to keep 'bugging' you with my ants, but... here's another example/bug that illustrates the frame rate render sync problem.

http://snap.berkeley.edu/snapsource/snap.html#present:Username=deader-tcssm&ProjectName=yoyo%20sean%20devgan

This is an older version, you'll notice the only 'wait' is inside the ants code. If you REMOVE that wait (or set it to 0.0), you'll see that the ants "skate" instead of "walk".

Maybe the forever loop is running TWICE between renders... so it's doing TWO 'next costumes' between rendered frames... so the ants "skate", never showing the other frame.

Putting the 'wait' in makes it render the two-frame animation cycle properly.

I thought every 'forever' would have an implicit yield at the bottom (or somewhere), to allow other forever loops to run.

Do you SEE the walk versus skate problem on YOUR machine? (It's hard to see on my slower laptop, but obvious on our Chrome Boxes).

deader69 commented 9 years ago

Another fascinating observation is that every clone is making A COPY of the block script.

I know because if you add/remove that bottom 'wait' while the app is running, subsequently cloned ants will either "walk" or "skate".

So the code is not statically 'compiled' once and shared amoung all instances, it's DUPLICATED, right?

bb010g commented 9 years ago

What about a global delay variable that could be adjusted depending on platform? On a slow machine it could be 0; on a fast machine it could be 0.1. Just stick in [wait (delay)] by the drawing commands.

jmoenig commented 9 years ago

@deader69 @bb010g please, set your preferences to "prefer smooth animations" when creating interactive multi-sprite games that you want to run the same on every computer. The scheduling is then taken care of by Snap (I seem to remember pointing this out a couple of days ago...)

deader69 commented 9 years ago

@bb010g adding a global delay variable is exactly what I did.

But really, just locking down the frame rate to a constant fps is all that's needed, Which is exactly what 'prefer smooth animations' does: Makes every morph run at 30 fps.

You can grep 'frameRate' and 'fps' in *.js to find the relevant bits of code. Every/any morph can set it's own fps too, for efficiency or responsiveness: bubbleHelp runs at only 2 fps, for example, while BouncerMorph runs at 50 fps.

In other words... do what Jens says! :-)