I believe the issue may be a weird thing with how you’re using Orientation. I heavily advise not using Orientation for something like this, and instead using CFrame.Angles as I have, with this as a solution:
local RunService = game:GetService("RunService")
-- Constants
-- When the player holds down the prompt
local TURN_SPEED: number = .5
-- When the player stops holding down the prompt
local TURN_BACKWARDS_SPEED: number = 4
-- Variables
local Turn_Value: number = 0
local Valve = script.Parent
-- Prompt is your ProximityPrompt
local Prompt = Valve:WaitForChild("ProximityPrompt")
local Original_Pivot: CFrame = Valve:GetPivot() -- This should be stored
local Connections: { RBXScriptConnection } = {}
-- For updating the valve angle
local function UpdateValve()
-- As a micro-optimization, only multipliy the original CFrame by the spin value if it's greater than 0
Valve:PivotTo(if Turn_Value > 0 then Original_Pivot * CFrame.Angles(0, 0, Turn_Value) else Original_Pivot)
end
-- Shorthand function for disconnecting all connections
local function DisconnectAll()
for Index: number, Connection: RBXScriptConnection in Connections do
-- Since :Disconnect() returns nil, this knocks out two things in one,
-- making the table index nil and disconnecting the connection
Connections[Index] = Connection:Disconnect()
end
end
-- Then, set up connections
Prompt.PromptButtonHoldEnded:Connect(function()
DisconnectAll()
-- Makes the valve spin back to its original orientation
table.insert(Connections, RunService.Heartbeat:Connect(function(Elapsed: number)
-- What math.max does is get the maximum value of two or more values
-- In this case, it's the Spin_Value subtracted by the turn speed and 0, which stops Spin_Value from going below 0
Turn_Value = math.max(Turn_Value - (Elapsed * TURN_BACKWARDS_SPEED), 0)
UpdateValve()
if Turn_Value <= 0 then
DisconnectAll()
end
end))
end)
Prompt.PromptButtonHoldBegan:Connect(function()
DisconnectAll()
table.insert(Connections, RunService.Heartbeat:Connect(function(Elapsed: number)
-- What this does is add the turn speed to Turn_Value
-- The modulo (%) operator makes it repeat if it's greater than or equal to pi
Turn_Value = (Turn_Value + (Elapsed * TURN_SPEED)) % math.pi
UpdateValve()
end))
end)
Result:
All of the code I provided should hopefully help you understand what does what. And, as a slight warning, the snippet CFrame.Angles(0, 0, Turn_Value)
may provide the wrong axis rotation. If so, just move it to a different 0, i.e CFrame.Angles(0, Turn_Value, 0)
. Here’s something funny that happened while I was trying to figure that out:
I hope this helps.
If you also wanted something that more directly correlates to the Prompt’s HoldDuration, and something with fixed math (oops…), try this:
local RunService = game:GetService("RunService")
-- Constants
-- When the player stops holding down the prompt
local TURN_BACKWARDS_SPEED: number = .5
-- Variables
local Turn_Value: number = 0
local Finished: boolean = false
local Valve = script.Parent
-- Prompt is your ProximityPrompt
local Prompt = Valve:WaitForChild("ProximityPrompt")
local Original_Pivot: CFrame = Valve:GetPivot() -- This should be stored
local Connections: { RBXScriptConnection } = {}
-- For updating the valve angle
local function UpdateValve()
-- As a micro-optimization, only multipliy the original CFrame by the spin value if it's greater than 0
Valve:PivotTo(if Turn_Value > 0 then Original_Pivot * CFrame.Angles(0, 0, math.rad(Turn_Value * 360)) else Original_Pivot)
end
-- Shorthand function for disconnecting all connections
local function DisconnectAll()
for Index: number, Connection: RBXScriptConnection in Connections do
-- Since :Disconnect() returns nil, this knocks out two things in one,
-- making the table index nil and disconnecting the connection
Connections[Index] = Connection:Disconnect()
end
end
local function FinishValve()
DisconnectAll()
Finished = true
Turn_Value = 0
UpdateValve()
end
-- Then, set up connections
Prompt.PromptButtonHoldEnded:Connect(function()
if not Finished then
DisconnectAll()
Prompt.Enabled = false
-- Makes the valve spin back to its original orientation
table.insert(Connections, RunService.Heartbeat:Connect(function(Elapsed: number)
-- What math.max does is get the maximum value of two or more values
-- In this case, it's the Spin_Value subtracted by the turn speed and 0, which stops Spin_Value from going below 0
Turn_Value = math.max(Turn_Value - (Elapsed * TURN_BACKWARDS_SPEED), 0)
UpdateValve()
if Turn_Value <= 0 then
DisconnectAll()
Prompt.Enabled = true
end
end))
end
end)
Prompt.Triggered:Connect(FinishValve)
Prompt.PromptButtonHoldBegan:Connect(function()
DisconnectAll()
table.insert(Connections, RunService.Heartbeat:Connect(function(Elapsed: number)
-- What this does is add the turn speed to Turn_Value
-- The modulo (%) operator makes it repeat if it's greater than or equal to pi
Turn_Value = (Turn_Value + (Elapsed * (1 / Prompt.HoldDuration))) % 1
UpdateValve()
if Turn_Value >= 1 then
return FinishValve()
end
end))
end)