Animation Not Looping

Visuals, audio, animation. Blended, not stirred. If LiveCode is part of your rich media production toolbox, this is the forum for you.

Moderators: FourthWorld, heatherlaine, Klaus, kevinmiller, robinmiller

Post Reply
Zephitron
Posts: 21
Joined: Sun Oct 10, 2010 6:04 am

Animation Not Looping

Post by Zephitron » Sun Oct 10, 2010 6:29 am

I'm new to RR (LiveCode), though in the late 80's and 90's I used HyperCard quite a bit.

Here's the little problem I'm having. I want to move some graphics across a window (card) and have them run continuously, going off one edge and appearing again at the opposite edge. A message will stop this behavior at some point.

So I've got my graphics passing across the screen, but a loop does not seem to make them continue. They go once then stop. I've tried various combinations of lockMoves set to true and false, and "without waiting" or not, to no avail. Using "set the lockMoves to true " at the beginning and "set the lockMoves to false " don't change the behavior at all.

Here's the code so far (the repeat 3 times is just for testing – want it forever in final version):

local sprite1Center, sprite2Center
local cardRight
local sprite1Speed = 100, sprite2Speed = 200

on mouseUp
put the width of this card into cardRight
put round((width of image "cloud1.gif")/2) into sprite1Center
put round((width of image "cloud2.gif")/2) into sprite2Center

repeat for 3 times
set the moveSpeed to sprite1Speed
// Reset cloud1 - move the image off-screen, just to the left of the card
set the Right of image "cloud1.gif" to 0
//Do the animation of cloud1, moving it just off screen:
move image "cloud1.gif" to cardRight+sprite1Center, 150 without waiting
//reset the position of cloud2 to just off screen:
set the Right of image "cloud2.gif" to cardRight
set the moveSpeed to sprite2Speed
//Do the animation of cloud2, moving it just off screen:
move image "cloud2.gif" to 0-sprite2Center, 150 without waiting
end repeat
end mouseUp

Why doesn't this repeat?

- Thanks
Eric

bn
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 4171
Joined: Sun Jan 07, 2007 9:12 pm

Re: Animation Not Looping

Post by bn » Sun Oct 10, 2010 11:41 am

Welcome to the forum Zephitron,

your script did work, it was just too fast for you to notice.
But working with a repeat loop for things that potentially run continously it blocks Rev/Livecode from doing anything else. Even if you put a "wait xxx seconds/milliseconds with messages" it is still not my preferred way. Rev has a thing called send in time, which sends a message at a given time to an object you choose. In the meantime Rev is free to do what it wants. The object then sends this message to itself in xxx time to repeat the action.

Your code looks like you do have enough experience in Hypercard to pick up on the concepts of Rev quickly so I attach (see remarks below) a stack that does show what is difficult to describe. Look at the scripts of the two images, at the script of the card and the script of the checkbox button. The downside of that approach is that it is more scripting in more places, the upside is you can run your animation without blocking the rest of your stack.
I used the syncRate to make the movement a little smoother. I set it to 10 and reset the syncrate to the default of 20 when the animation finishes. Watch out the dictionary is not correct in describing the syncrate. A lower sncrate uses more processor power and makes movements smoother.
In your original script you declare some variables at the top of the script. In Rev this makes them to script local varibles as different of local variables. Local variables are declared insider a Handler and they are only accessible to this one handler. Script local variables in contrast are declared outside of Handlers in the script. Those variables are accessible from every handler (command or function) within the same script. I took the habit of prepending the script local variables with an s and the local varibles with a t. Script local variables are in my view in almost all instances more convenient than global variables. Where I can not use script local varibles I use custom properties to store data that has to be accessed from different scripts.

Have a look at the stack and the dictionary/user documentation for things that are not immediately obvious. And of course please ask the forum if you get stuck.

I have the feeling that once you see what Rev does differently from Hypercard you are going to like Rev. (although it has some quirks, granted. See the remark about the dictionary above...)
regards
Bernd

somehow the forum does not let me upload the zipped file, no idea why because zipped files below 250KB used to work fine, I will try later and if that does not work I see what I will do.

Edit: removed the link to my site and uploaded the stack as attachement here.
Attachments
moveClouds.rev.zip
(94.83 KiB) Downloaded 440 times
Last edited by bn on Mon Oct 18, 2010 1:41 pm, edited 1 time in total.

Zephitron
Posts: 21
Joined: Sun Oct 10, 2010 6:04 am

Re: Animation Not Looping

Post by Zephitron » Sun Oct 17, 2010 6:07 am

There are many interesting things to chew on and learn about here. I've been doing a bit of studying and playing around. For the benefit of other readers:

Basically, we are dealing with the fact that a computer can only do one thing at a time, and everything is on a clock.
So to make it *look like* more than one thing is happening at time, and to allow the computer to do other things, we have to play some tricks and do some juggling.

You'r right - it did run three times. I tested it. One only sees the last run.

I see now the logic of putting the script in the card. It is reflective of the fact of the message hierarchy.
I redrew the message diagram from the docs (http://www.runrev.com/developers/lesson ... g-started/), since it was very hard to see. I posted it here: http://www.ericplatt.com/livecode/Message_Diagram.pdf).
For example, handlers that are triggered in buttons (or any "control", such as an image, on a card), can call upon scripts in the card.

A couple of questions I have:
First, when does one want to place the script in the stack instead of the card – only when there are multiple cards? And is there an equivalent to the Background that HyperCard had?

Second, when is it appropriate to "send" a handler message rather than just call it. In other words, instead of

on mouseUp
if the hilite of me then
send startClouds to card "cloudcard0" of stack "Animation Explorer"
else
send stopMovingClouds to card "cloudcard0" of stack "Animation Explorer"
end if
end mouseUp

you could just say this (and it works), since the card is in the message path:

on mouseUp
if the hilite of me then
startClouds
else
stopMovingClouds
end if
end mouseUp

…because I notice they say in the docs: " Note: Using the send command is slower than directly executing the commands using the normal message path. For best efficiency, use the send command only when you want to delay the message or when the handler you want to execute is not in the message path."

Also, I'm not sure why you have the startClouds handler call moveClouds with a delay –
send moveClouds to me in 2 milliseconds
I thought this technique would be appropriate if a routine is calling itself (making a loop, like the moveClouds routine does), but why when it's calling another routine? It will work without the delay:

send moveClouds to me

Breaking the handlers down into start, stop, and move I realize is a widely applicable technique, whenever you have a running process, and need to allow other things to happen. I thought it'd be fun to make a little clock, and it turns out this technique is perfect for it. For example, one could do this (not using that technique) but then it's not obvious how to cleanly stop the clock:

local stopClock

on runClock
put the long time into field "time"
send "runClock" to me in 1 seconds
//if stopClock = true then exit runClock ?? not that doesn't work
end runClock

Do this instead:

local keepRunningClock = false

on startClock
put true into keepRunningClock
runClock
end startClock

on stopClock
put false into keepRunningClock
runClock
end stopClock

on runClock
if keepRunningClock = true then
put the long time into field "time"
send "runClock" to me in 1 seconds
end if
end runClock

And obviously you have start and stop buttons with something like:
on mouseUp
send startClock to card "clock"
end mouseUp

on stopClock
put false into keepRunningClock
runClock
end stopClock

Again though, one could simple say "startClock" without the "send" and it would work, since the card script is in the path of the message hierarchy.

(For the benefit of other readers:) One could vary the timing, or a distance or other increment inside of the loop in order to speed up or slow down a process. For example, if you wanted a scrolling field, the speed of the scroll could be changed with the send timing:

on doScroll
if scrollTheField = true then
put scrollDistance+scrollIncrement into scrollDistance
set the vScroll of field "prompt_field01" of this stack to scrollDistance
send "doScroll" to me in 100 milliseconds
end if
end doScroll

And change the speed by varying scrollIncrement. If the milliseconds are too large, it would look jerky, so it has to be fast enough to not be irritating. I might want to vary the m,illiseconds downward however… so somehow mix the two ways of varyingt the speed.

Does this seem like a good way to scroll a field dynamically and continuously? I'd like the user to be able to change things (like font size, speed, etc.), on the fly.

But this raised the question in my mind: if you send the move command (or any command in this kind of loop where it calls itself with "send" with a delay) and it's already loopinp, what happens? Because I noticed the scolling got faster if I clicked on the scroll button more than once.
I tested this with the clock stack, using the Message Watcher (Development menu) and discovered that it generates multiple runClock loops. This makes sense, but it was rather surprising and amazing (that the loop could be running concurrently many times). Then I suddenly understood why we need the code that uses the pendingMessages function, like you put in the stack you posted.

if runClock is not in the pendingMessages then
send "runClock" to me in 1 seconds
end if

This will allow only one loop to be running at a time. (The scroll routine needs the same thing)

Another thing I'm trying to puzzle out is the "stop moving" command in relation to move when used with either "without waiting" or not.
For example, if I had the following handler:

on doMove
if runMe then
// Move left:
move graphic "circle" to 0,cardLeftYCenter // If this has "without waiting" the graphic isn't moved to this position
-- because the whole handler (not just the statement) continues without waiting for it. But with no
--"without waiting", the handler pauses until the move is complete
// Move right:
move graphic "circle" relative cardRight-25,0 without waiting // This one is relative (so that it's relative to the initial positioning)
end if
end doMove

And to keep it going back and forth:

on moveStopped
send doMove to me in 10 milliseconds
end moveStopped

This works fine to move the graphic back and forth continuously, but if I use this to stop it:

on StopIt
stop moving graphic "circle"
put false into runMe
end StopIt

it works ok to freeze the graphic in it's tracks when it's moving right, but if it's moving left, it will stop, then move off to the right, off the edge of the card.
This has to do with the use of "without waiting" (or lack of) command, but it's a bit fuzzy to me. Apparently the first move command is halted because it executing and the handler is waiting for it to complete, yet the second move command, with the "without waiting" is still "in the queue" as it were, and subsequently fires off? Am I on the right track?

Moving on (no pun intended), the next thing I'd like to do with this stack is have some UFOs float in from random directions, and then the user be able to grab them and drag them to a goal, avoiding clouds, and bring them down to a “goal”. It should be easy enough figure out the random and coordinates part. For collision detection though, should I use the intersect function? And in order to drag and move an image are the dragStart dragData functions the appropriate ones to investigate?

Thanks!

bn
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 4171
Joined: Sun Jan 07, 2007 9:12 pm

Re: Animation Not Looping

Post by bn » Sun Oct 17, 2010 10:45 am

Hi Eric,
glad you figured out most of it.
A couple of questions I have:
First, when does one want to place the script in the stack instead of the card – only when there are multiple cards?
Your drawing of the message path is a clue to when to place a script where. If you want a handler to be accessible from any script in the stack you put it into the stack script. Also if you want it to be accessible from any handler from a substack you would place a handler in the stack script. Also if you want to use the stack as a library you put a handler in the stack script since only the stack script is in the message path if you "start using stack myStack". Other than that I place the handlers where their scope is. I.e. I often keep them with the object I am working on, using script local variables (the onces you declare outside of any handler in the script, most often at the top of the script) to store values between runs. On the other hand in the move clouds example I chose the card script, since it is the logical place since moving things on the card happens to this card.
And is there an equivalent to the Background that HyperCard had?
Yes, if you create a group with all the objects you want to show up on any newly created card you check the "behave like a background" or "backgroundbehavior" for that group in the property inspector. If you later want to add objects to the group you defined as a background and you want these objects to appear on all the cards that use this background then use "copy button "toCopy" to group myBackGroundGroup"
Second, when is it appropriate to "send" a handler message rather than just call it
there are two versions of the send command on with "in xxx time" and one without. The main difference being that without a send in time the current handler sends the message to the object you define runs that handler and then returns to the originating handler. If you use the send in time then first the calling handler finishes and then the message queue fires the send off to the object. So without in time the calling handler is still active, with in time the handler finishes. As you found out this allows you to do things "in parallel" although they are still bound to the one clock they depend on.
Second, when is it appropriate to "send" a handler message rather than just call it...
you could just say this (and it works), since the card is in the message path:
on mouseUp
if the hilite of me then
startClouds
right. I used the send to make you aware of the card script where this handler resides. It is not necessary and even slows it down a bit. And note when you issue startClouds from the mouseUp handler the mouseUp handler finishes after the startCloud handler is done. The startCloud handler has a " send moveClouds to me in 2 milliseconds" in it that starts the actual animation. I probably could have said 0 milliseconds, or 1 milliseconds, just as well. It is kind of a habit to give Rev some room for housekeeping, since that is what Rev does if no handler is executing. It takes care of memory and stuff like that. And Rev at times needs a little room for that especially in very long repeat loops. That would be the place to place a "wait 0 milliseconds with messages" lets Rev do its thing and that also makes the repeat loop resonsive to e.g. command period to stop execution in a runaway loop.

Back to the clouds. The startClouds handler does the nessesary initialisation of for the moveClouds handler. A handler that calls itself periodically should have as little code as necessary because you want it to be as fast as possible.
Does this seem like a good way to scroll a field dynamically and continuously? I'd like the user to be able to change things (like font size, speed, etc.), on the fly.
For just scrolling of a field this is ok. But if you want to let the user change fontSize then you automatically change the scroll of a field, you would have to catch that and adjust accordingly, no small feat. The magic words here are formattedHeight, margin, height of field, Number of lines of the field and the textHeight of the field then calculate the scroll from there.
Speed is be a lot easier, as you already did in your script.
Moving on (no pun intended), the next thing I'd like to do with this stack is have some UFOs float in from random directions, and then the user be able to grab them and drag them to a goal, avoiding clouds, and bring them down to a “goal”
this is no small feat. There is Malte Brill's excellent Animation Engine that helps with collision detection and movement etc. It is well worth checking out if you want to go the way of a sort of game beyond simple animations. Especially the collision detection is tricky since Rev's intersect does report whether the rects of two graphics intersect, but often you have a graphic that is smalller than the rect and it will report collision for the rects whereas you actually would want a collision reported on the visible part of the graphic only.
And in order to drag and move an image are the dragStart dragData functions the appropriate ones to investigate?
I would go the "grab" way. On mouseUp grab me. And in a on mouseMove x,y handler do the collision detection.

OK, I tried to answer most of the questions. It might be easier to break this up into individual threads. For the
it works ok to freeze the graphic in it's tracks when it's moving right, but if it's moving left, it will stop, then move off to the right, off the edge of the card.
This has to do with the use of "without waiting" (or lack of) command, but it's a bit fuzzy to me. Apparently the first move command is halted because it executing and the handler is waiting for it to complete, yet the second move command, with the "without waiting" is still "in the queue" as it were, and subsequently fires off? Am I on the right track?
problem, could you post your stack?

regards
Bernd

Zephitron
Posts: 21
Joined: Sun Oct 10, 2010 6:04 am

Re: Animation Not Looping

Post by Zephitron » Mon Oct 18, 2010 4:19 am

Here you go – I've attached the file. I was trying to see how simple I could make the moving back-and-forth of a graphic. I wanted the graphic to stop in it's tracks, then start again from where it was.
It's in this directory: http://www.ericplatt.com/livecode/

bn
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 4171
Joined: Sun Jan 07, 2007 9:12 pm

Re: Animation Not Looping

Post by bn » Mon Oct 18, 2010 1:00 pm

Hi Zephitron,
I made some changes to your stack. Now it moves graphic "circle" in a non-blocking manner. Before when you issued the doMove statement the first part, moving left it was blocking since the handler had to wait until the movement was finished. Then it would move right. If you stopped it you set the runMe to false. If it was moving left the movement and the following movement to the right was not affected by runMe being false. But if you stopped during movement to the right, which was "without waiting" that handler finished and once you set runMe to false could not be triggered by a doMove command.

I attache a little stack that does the movements and if you stop it it moves to the right, regardless of where it was headed. After rereading your post you wanted
the graphic to stop in it's tracks, then start again from where it was
if you have problems implementing that please say so.
regards
Bernd
Attachments
circle1-bn.rev.zip
(2.38 KiB) Downloaded 389 times

Zephitron
Posts: 21
Joined: Sun Oct 10, 2010 6:04 am

Re: Animation Not Looping

Post by Zephitron » Fri Oct 29, 2010 12:45 am

I've attached the game demo (work in progress) – just a fun thing that uses some photography references that some photography friends will be amused by (haven't put all the photos in yet).

Here's the problem now:
I've been trying to implement an intersect function so that the UFO, after being grabbed while flying and brought near the "troy" figure, will be placed on the ground. The idea is that the user will capture a number of the UFOs until a certain limit (after which they will get stacked up and something else will happen).

It either hangs or I get an error:

"mouseMove has reached the recursion limit of 40000. "
I know what recursion is but don't see why the mouseMove handler would be considered as calling itself.

In regards to the Animation Engine, I'm reluctant to use them because it would be disempowering compared to building and learning "from scratch". By doing this myself I will really know my tools and have the flexibility of being able to make them and change them the way I need for my specific purposes.
Attachments
animation_explore.rev.zip
(211.93 KiB) Downloaded 400 times

bn
VIP Livecode Opensource Backer
VIP Livecode Opensource Backer
Posts: 4171
Joined: Sun Jan 07, 2007 9:12 pm

Re: Animation Not Looping

Post by bn » Fri Oct 29, 2010 3:39 pm

Hi Zephitron,
you are wrestling with different issues here I guess. On mousemove you are trying to set the loc of the ufo and the grab is still active, then when you set the loc of the ufo you also trigger a mouseLeave. I think those are the main culprits, although I did not have the time to analyze it further.
I would break this down to a stack that just looks at the UFO. (those guys know how to fly an UFO, by the way, they are hard to catch) and think about the start and stop mechanism. I would also move the placement of the UFO to a mouseUP handler. But then again I dont fully understand the game.
Maybe this gives you some hints, areas to explore.
regards
Bernd

Zephitron
Posts: 21
Joined: Sun Oct 10, 2010 6:04 am

Re: Animation Not Looping

Post by Zephitron » Fri Oct 29, 2010 5:59 pm

Good Morning Bernd ,

I was releuctant to make a separate stack to work on this function, thinking it would be simple (and wanting to keep it simple), but it's no so simple as it appears. Sounds like I need to break it down into smaller chunk tasks. Need to think about the sequence of events more closely.

Moving the placement to a MouseUp sounds like a good idea, since there are all these things going on during the mouseDown, and the placement doesn't need to be one that the program is trying to do.

Post Reply