Yielding until Motor.DesiredAngle is met

As a developer, it is currently difficult to yield a thread until the DesiredAngle property of a Motor is met, demanding some improvisation with BindableEvents (at least to prevent yielding with repeat wait()).

My proposition comes about from both finding the lack of such a method (a DesiredAngleReached event, perhaps?) inconvenient for working with motors and reading an article by @Kampfkarren about avoiding the use of wait() when needing to yield a thread in situations like these, where he writes:

“Roblox has events for everything you could possibly be waiting for (and if it doesn’t make a feature request!)”

A rather makeshift and cumbersome workaround I fell back on, like I said, made use of BindableEvents and GetPropertySignalChanged:

local desiredAngleReachedEvent = Instance.new("BindableEvent")

Motor:GetPropertySignalChanged("CurrentAngle"):Connect(function()
    if Motor.CurrentAngle == Motor.DesiredAngle then
        desiredAngleReachedEvent:Fire()
    end
end)

desiredAngleReachedEvent.Event:Wait()
print("Desired angle reached")

This, as suggested in the aforementioned post, could instead be an event of Motors themselves:

Motor.DesiredAngleReached:Wait()
print("Desired angle reached")
1 Like

Motor.DesiredAngle == Motor.CurrentAngle could never be met, so your script would yield forever if you are waiting for such a signal. Not sure if that’s great practice for your code base. Also note that these are float properties, so you have to check within an epsilon threshold anyhow – they might not ever be exactly the same value.

The code is somewhat trivial for this. I think you are misinterpreting the advice to avoid wait() and using property changed events where you don’t have to (and I don’t think GetPropertyChangedSignal even works for physics properties(?)). You should avoid wait(), but you don’t need to avoid yielding using more well-timed events like Heartbeat/Stepped. That’s fine to use here.

This should work and is relatively clean:

local Heartbeat = game:GetService("RunService").Heartbeat -- or Stepped

function yieldUntilDesiredAngle(motor, timeout, minDeviation)
   -- TODO: default timeout and minDeviation to sensible values (i.e. 30, 0.01)
   local start = tick()
   while tick() - start < timeout and math.abs(motor.CurrentAngle - motor.DesiredAngle) > minDeviation do
      Heartbeat:Wait()
   end
   return tick() - start
end

This is clean because physics properties change every frame anyway. It’s unnecessary to use an event here to listen for CurrentAngle changes when not using an event is less roundabout/cumbersome and has the same effect (since the changed event would fire every frame, anyway).

2 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.