I got a version of snake working with the threepenny-gui
library, but I didn't like the fact that I was explicitly calling newEvent
and addStateUpdate
manually instead of defining the behavior completely based on events, e.g. this:
(updates, addUpdate) <- liftIO newEvent
managerB <- accumB initialManager updates
on UI.tick timer $ \_ -> addUpdate $ \manager -> manager'
compared to:
managerB <- accumB initialManager $
UI.tick timer $> \manager -> manager'
IIUC the second is more idiomatic FRP, as it defines a behavior with the actual event instead of creating a proxy event to proxy updates through. But when I make this change, it causes one of two problems:
- If I define
managerB
first (using RecursiveDo to accesstimer
, which is defined below), nothing's rendered at all - If I move
managerB
to the end (using RecursiveDo to accessmanagerB
from the DOM elements), the initial movement when hitting an arrow key for the first time lags, and the frames render in a jerky fashion.
Am I doing something wrong? What's the idiomatic way I should structure these events/behaviors?
Code diff here: https://github.com/brandonchinn178/snake/compare/inline-event-handlers
A not particularly pretty workaround for the jerkiness, which I tested on the alternate branch in your repository, is, so to say, using both approaches at once: re-firing the tick event and using that instead of
UI.tick timer
to definemanagerB
:The issue appears to be that plugging
UI.tick timer
directly into the event network somehow gets in the way of Threepenny sending the JavaScript calls needed to update the UI in a timely way. The indirection in usingon
withfireTime
(which, in particular, should meantimeE
happens notionally afterUI.tick timer
) seems to skirt around the problem. A less intrusive workaround would be, instead of introducingtimeE
, explicitly callingflushCallBuffer
in a handler forUI.tick timer
; in my tests, however, that reduced the jerkiness a lot but didn't eliminate it completely. (See also threepenny-gui issue #191 for possibly relevant background information.)As for the delay on the first keystroke, it appears that can be eliminated by moving your invocation of
UI.start timer
to the very end ofgui
, aftermanagerB
and the rest of your event network is set up.(On an additional note, it is probably a good idea to follow the recommendation of the
Graphics.UI.Threepenny.Timer
docs and set-threaded
in theghc-options
for compiling your executable, even if that doesn't seem to have an effect on the problem you describe here.)