I’m scripting a swingset that counts the amount of times it has reached an angle greater than 45 degrees:
---server script
local Spin = script.HingeRef.Value --ObjectValue ref to HingeConstraint
local SwingCounter = script.Parent.TextLabel
local count = 0
Spin:GetPropertyChangedSignal("CurrentAngle"):Connect(function()
if Spin.CurrentAngle > 45 then
print("angle is >45 degrees")
count = count + 1
SwingCounter.Text = count
end
end)
For some reason, this function didn’t work at all–nothing was printed, and no errors came up in the dev console. I then wrote something similar using a while loop:
while Spin.CurrentAngle > 45 do
wait()
print("angle is >45 degrees")
count = count + 1
SwingCounter.Text = count
end
This didn’t work either, so I put a conditional inside the while loop instead:
while true do
wait()
if Spin.CurrentAngle > 45 then
print("angle is >45 degrees")
count = count + 1
SwingCounter.Text = count
end
end
This was the only one that worked, but I’m confused. Why does the third script work, but not the other two? I don’t really want to use this solution because I intend on having multiple swings on my game, and I’d imagine that having multiple while loops running in multiple server scripts would not be very efficient. Is there any way I can optimize this?
The Second one doesn’t work because it will keep on looping until after the CurrentAngle is greater than 45 degrees then it will stop. The First one doesn’t work because I think the GetPropertyChangedSignal has been having issues lately but I can’t confirm it is true.
The third one is a fine way to have it work from what I can tell because it constantly loops.
In working with HingeConstraints I’ve noticed that when you run the game in Test mode you only see the changes each time you click on the Constraint, it doesn’t update automatically.
I noticed that as well–but if you were to print the CurrentAngle in small intervals while moving the swing, you’d get this:
…which suggests that the property updates fairly often, if not automatically. If :GetPropertyChangedSignal() isn’t catching any of these changes, then the issue must be rooted in something else.
You could use a single RunService.Stepped and a list of the HingeConstraints you need to “observe”, to avoid having multiple threads.
The following example code contains a few different concepts and features, so do please take your time to read, study and experiment with it.
--
local ObservedHingeConstraints = {}
local function AddObservation(hingeObj, thresholdFunc, callbackFunc)
-- Add/update the dictionary of observed hinges
ObservedHingeConstraints[hingeObj] = {
thresholdFn = thresholdFunc, -- `IsHingeAnglePast45`
callbackFn = callbackFunc -- `UpdateHingeCountAndText`
}
end
local function RemoveObservation(hingeObj)
-- Remove hinge-object from being observed
ObservedHingeConstraints[hingeObj] = nil
end
local RunService = game:GetService("RunService")
RunService.Stepped:Connect(function()
-- Loop through all "observed" hinge-constaint objects
for hingeObj,dataFns in pairs(ObservedHingeConstraints) do
-- If the given threshold-function for this hinge-constaint object
-- returns true, then call the callback-function
if dataFns.thresholdFn(hingeObj) then
dataFns.callbackFn(hingeObj)
end
end
end)
--
--
local HingesToggledState = {}
local function IsHingeAnglePast45(hingeObj)
-- NOTE: This expression may need adjustments, depending on how the hinge-constraints
-- and their attachments are oriented within the game-world, so the "counts" will
-- work as intended.
local isPastThreshold = (hingeObj.CurrentAngle < -45) or (hingeObj.CurrentAngle > 45)
-- To avoid being triggered every `Stepped`-iteration when past threshold,
-- then verify if there is a need to actually return true this time also.
if HingesToggledState[hingeObj] then
-- Did the hinge get below threshold again?
if not isPastThreshold then
-- Remove hinge from dictionary, so it can be triggered again later
HingesToggledState[hingeObj] = nil
end
elseif isPastThreshold then
HingesToggledState[hingeObj] = true
return true
end
return false
end
--
local Counters = {}
local HingesTextLabels = {}
local function UpdateHingeCountAndText(hingeObj)
local newCount = 1 + (Counters[hingeObj] or 0)
Counters[hingeObj] = newCount
local textLabel = HingesTextLabels[hingeObj]
if textLabel then
textLabel.Text = newCount
end
-- For debugging
print(hingeObj.Name,"count:",newCount)
end
--
local function ObserveHinge(hingeObj, textLabelObj)
Counters[hingeObj] = 0
HingesTextLabels[hingeObj] = textLabelObj
-- Add this hinge-constraint object to the list of observed hinges,
-- with a particular threshold-function, and a callback-function
AddObservation(hingeObj, IsHingeAnglePast45, UpdateHingeCountAndText)
end
--
-- For this example, we're just finding all HingeConstaints in the Workspace,
-- without considering any associated text-label objects.
local i=0
for _,obj in ipairs(game.Workspace:GetDescendants()) do
if obj:IsA("HingeConstraint") then
-- Change name of HingeConstaint, to distinguish them from
-- each other, when debugging.
i = i + 1
obj.Name = ("%s (%i)"):format(obj.Name, i)
-- Add hinge to the observation feature
ObserveHinge(obj, nil)
end
end