If you think about this based on player reaction time, you want the boat to be responsive. I somewhat disagree that looping like this is wasteful, simply because it’s important that you do not add too much delay. Anything above 0.1 seconds for controls is cutting it close because a lot of people have reaction times less than that, and generally I’ve found that looping about 50%-75% of a player’s reaction time yields better feeling controls, so I’d disagree that wait()
is wasteful in this situation.
wait()
is not necessarily more or less expensive than heartbeat, after all both are simply activating the execution of code, and wait does so through yielding, while Heartbeat, being an event, creates a new thread (this could be seen as more expensive, however you can connect thousands of events and never run into issues since this isn’t particularly very expensive).
I would actually very much so suggest using Heartbeat none the less. Heartbeat runs immediately before every physics step, while waiting will mean you are completely out of sync with heartbeat. An example of how sync issues can cause problems comes up with monitors. If your computer is sending visual data to the monitor when the monitor is not ready to display, you can introduce slight tearing or the appearance of low FPS which may not even be noticeable, and similarly you can introduce unexpected results in controls, which while subtle, I’ve found to effect my experience somewhat.
Here are some important things about Heartbeat and Stepped: Heartbeat runs before physics, so making position or physics changes on Heartbeat will yield expected results. Stepped runs after physics, but before anything is replicated to clients. This has very strange implications on what clients will see when changing physics on Stepped, for example, the position of a part can appear completely different than where it is when physics are taking place causing the appearance of extreme lag when none is present, but Stepped is particularly useful if you’re looking to do that in the first place, as well as being a useful marker to know when physics calculations end.
Finally, if you do in fact want to slow down input, you can actually combine both a wait
call and sync with physics:
-- Controller loop
wait(0.1) -- Slow down!
RunService.Heartbeat:Wait() -- This will wait at most one frame longer, however, now you're fully synced with physics so when things get replicated after Stepped fires shortly after things can appear much more accurate to players
-- Some physics stuff
Also side note: TweenService now uses Heartbeat anyway, so using Tweens and Changed events with a tweened object is effectively the same as using Heartbeat with the extra overhead of replication and property changes in general. Rather than Tweening an object, you can use TweenService:GetValue()
instead on a Heartbeat loop or connection as a nice optimization that additionally yields less overall clutter.