Checking for CurrentAngle Updates on HingeConstraints?

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?

1 Like

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:
image
…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 

Just studied this for a good while–I’ll be sure to use this! Thank you! ^^