Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to prevent overlapping animations using core.async?

I have this loop handling animations for a character -- set-image! just takes a key and displays the appropriate image.

(defn main-animation-loop []
  (go (while true
        (set-image! :normal)
        (<! (timeout 8000))
        (set-image! :blink)
        (<! (timeout 150)))))

Every once in a while the character needs to do some special actions. This should be able to interrupt the main animation:

(defn dance! []
  (go (set-image! :look-left)
      (<! (timeout 1000))
      (set-image! :look-right)
      (<! (timeout 1000))
      (set-image! :wave)
      (<! (timeout 2000))))

What's a good way to pause the main animation while the dance routine is happening?

like image 936
yayitswei Avatar asked Nov 26 '25 20:11

yayitswei


1 Answers

It's common in CSP style programming to pass a control channel to event loops so you can, at the very least tell them when to stop. In this case if there was a control channel that went to main-animation-loop and you gave a copy of that to dance!, then dance could tell main-animation-loop to pause and unpause appropriatly. Or stop it then start it again (passing the same control channel in case others are using it).

I use a pattern like this to check for messages each time around the event loop:

(go (while (not= control-chan
                 (second (async/alts! [control-chan (async/timeout arg)])))
           (do-stuff-here))

This checks to see if it was the timeout or the control channel that caused the interruption.

(defn main-animation-loop [control-chan]
  (go (while (not= control-chan
                 (second (async/alts! [control-chan (async/timeout 150)])))
        (set-image! :normal)
        (<! (timeout 8000))
        (set-image! :blink))))

(defn dance! [control-chan]
  (go (!> control-chan :stop)
      (set-image! :look-left)
      (<! (timeout 1000))
      (set-image! :look-right)
      (<! (timeout 1000))
      (set-image! :wave)
      (<! (timeout 2000))
      (main-animation-loop control-chan)))

By passing the same channel to both functions, you allow them to communicate with each other. main-animation-loop will keep looping as long as all the messages it receives are coming from the timeout channel. As soon as it sees that one of them came from the control channel instead of the timeout, it will stop. This allows dance! to tell it to stop by sending any message to the control channel. In similar code I often have the main event loop check the content of the message and do more than just stop, though in this case stopping is enough because dance! can simply start the event loop again.

like image 126
Arthur Ulfeldt Avatar answered Nov 28 '25 14:11

Arthur Ulfeldt



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!