How to create a throttle system?

the problem with that If statement, is that throttle might not be nil, but currentThrottle apparently is, and you’re not checking it due to the OR

perhaps that should be AND

I think its not finding your attribute, maybe your parent.parent… isn’t getting the right object which contains the attribute. I suspect this because:

GetAttribute

Function of:

Instance

Description:

This function returns the attribute which has been assigned to the given name. If no attribute has been assigned then nil is returned.

from: Instance | Roblox Creator Documentation

JESUS CHRIST LORD ALMIGHTY SIR.
That is a BIG ERROR. I hope this was marked solved without, ahem… the lag issue.
The reason I react as such is as follows:

local RunService = game:GetService("RunService")

local usp = game:GetService("UserInputService")

local throttle = script.Parent.Parent.Parent:GetAttribute("Throttle")
local currentThrottle = script.Parent.Parent.Parent:GetAttribute("currentThrottle")

throttle = 0

local function throttle(step) -- this is being called every frame.
	wait(0.5) -- yields do not work in the RunService pipeline events.
	local function Input(userinput) -- wait a minute...
		
		if currentThrottle > 100 then -- redundant
			currentThrottle = 100 -- redundant
		end -- redundant

		if currentThrottle < 0 then -- redundant
			currentThrottle = 0 -- redundant
		end	-- I've said it enough so let me explain.

        -- There's an awesome math library that has been expanded by ROBLOX;
        -- so remove all that redundancy and do this simple one-liner
        currentThrottle = math.clamp(currentThrottle, 0, 100) -- Ta-da!

		 -- separating these input checks is going to make it always eval twice
		if userinput.KeyCode == Enum.KeyCode.LeftShift then
			throttle = 1 
		end
	    -- elseif, remove the end above this. no point in separation here.
		if userinput.KeyCode == Enum.KeyCode.LeftControl then
			throttle = -1 
		end
        -- okay so let's discuss why you were pointed towards RunService..
        -- ..pipeline events.
        -- They are more reliable than "wait" (btw use task library task.wait)
        -- and two, they provide the frametime (deltatime/step)
        -- you may be asking: "Ok, so what? What does that allow for?"
        -- well my student, let me tell you.. (this is what you likely wanted)
        -- CurrentThrottle + ((ThrottlePerSecond*Step)*throttle)
        -- Your CurrentThrottle will increase by ThrottlePerSecond due to
        -- the fact that deltatime is time between the frames.
        -- This means, the product of UnitsPerSecond * dt (deltatime)..
        -- will ALWAYS take a second. And it will be about as smooth as
        -- the client FPS. 1/60 is roughly 0.0166~7 so 20*(1/60) will be a
        -- 1/3 increase to the currentThrottle per frame, adding up to the
        -- previous number of 20 after 60 frames, or 1 second of time.
		currentThrottle = currentThrottle + throttle -- Anyways, there's a much
	end -- bigger issue at hand here.
	usp.InputBegan:Connect(Input) -- oh dear god no. this. this is the lag.
    -- you're connecting this event 60 times per second (at first, which then
    -- quickly drops you to FAR below 60 frames per second.)
    -- You should have the input handle the throttle variable, and then this
    -- event handle the CurrentThrottle calculations based on the 
    -- value of throttle.

    -- Meaning, the input is handled outside of the throttle function. This is
    -- simply dependant on the input changing the "throttle" variable.
end	
RunService.RenderStepped:Connect(throttle)

-- also the error was caused because you named your function "throttle."
-- you cannot ever overlap variables in the same scope and expect it to work.
-- in function throttle, "throttle" is not defined before the function. 
-- Basically the function overwrites it, but due to it being localized, the
-- functions scope cannot actually see it, and determines it as nil.

while wait(0.3) do -- task.wait is pretty pog.
	print(currentThrottle, "Throttle")
end

Not trying to be rude, just a lot of “What am I looking at right now?!”

I hope this helps, though I hope far more that it wasn’t needed and you did actually get it figured out.

EDIT: foggy sleepy brain mistakes

that’s the program I first tried… I fixed the lag but the error still happens

My current script: (line 22 is where the error is (currentThrottle = currentThrottle + throttle))

Any help?


local usp = game:GetService("UserInputService")

local throttle = script.Parent.Parent.Parent:GetAttribute("Throttle")
local currentThrottle = script.Parent.Parent.Parent:GetAttribute("currentThrottle")

local function throttleFunction()
	local function Input(userinput)
		if userinput.KeyCode ~= Enum.KeyCode.LeftControl and userinput.KeyCode ~= Enum.KeyCode.LeftShift  then
			throttle = 0
		end
		
		if userinput.KeyCode == Enum.KeyCode.LeftShift then
			throttle = 1
		end
	
		if userinput.KeyCode == Enum.KeyCode.LeftControl then
			throttle = -1
		end
		
		currentThrottle = currentThrottle + throttle
		
		print(throttle)
	end
	usp.InputBegan:Connect(Input)
end
RunService.Heartbeat:Connect(throttleFunction)

1 Like

I’m sorry but you completely missed why I replied before; and yes I will assist you.

I did not at any point send you modified code in that post. I simply wrote instructions (comments are not useless,) that you did not read; so –
please read the following very carefully.

You are connecting an event that does not disconnect itself. You are not disconnecting it anywhere.
Not only that, you are then connecting it to RUNSERVICE, an event tied to every frame rendered.
FPS Unlocker would make it very evident that after a while, the lag will return and ultimately crash the ROBLOX player, possibly even Studio (although studio is 64 bit so it can handle much more ram. Unlikely to happen.)
Now here’s the catch:
This is not only awful practice, it’s a memory leak. If you didn’t know: a memory leak, is hell. It can cripple a game because of its existence. Tapping Gods (post rewrite) is an example of this. Player base hated it, rework is much nicer.

Your error is caused because you’re connecting the local function Input to UserInputService in a unique scope (from heartbeat/renderstepped/or stepped) which does not automatically grab the environment values you’re trying to access, meaning the Input function doesn’t actually have access to “throttle” or “currentThrottle.”

Now, moving along. You should fix your script like so.

local uInputService = game:GetService("UserInputService")
-- code is structured logic. be descriptive with variable name scheme, you want
-- your code to be easily maintainable. 'uInputService' v. 'usp'

local This = script.Parent; -- this is something you absolutely must get the
-- hang of. Repetitively relying on absolute paths is a clear sign of a novice
-- which is undesirable for even the freshest of learners to the eldest of such.
local ThatFirstParent = This.Parent;
local ThatSecondParent = ThatFirstParent.Parent;

local ThrottleDirection = ThatSecondParent:GetAttribute("Throttle") or 0
local ThrottleValue = ThatSecondParent:GetAttribute("currentThrottle") or 0
local MaxThrottle = 100
local ThrottleIncPerSecond = 20 -- 4 seconds to get to 100.

local KeysDown = {
   LShift = false,
   LCtrl = false
} -- this will be used later on.

-- now this is where it gets from light teaching to a full lecture, read along
-- and I'll teach you why your original code will never,
-- yes, I repeat; never work.
local LeftShiftKey = Enum.KeyCode.LeftShift -- first tidy up these, not needed but
-- very nice and easy to maintain.
local LeftControlKey = Enum.KeyCode.LeftControl

local function HandleKeyPressed(Input, Handled)
   if Handled then return end -- you do not want your control keybinds to fire
   -- if a textbox is being typed in, or if the user presses a button.
   local Key = Input.KeyCode
   -- Now, I will demonstrate everything that you need to do.
   if Key == LeftShiftKey then
      KeysDown.LShift = true -- key buffer so we can make accurate calculations.
   elseif Key == LeftControlKey then
      KeysDown.LCtrl = true -- see above comment
   end
end

local function HandleKeyReleased(Input, Handled)
   if Handled then return end -- same ordeal as the previous function; typing/button = halt user input
   local Key = Input.KeyCode
   -- Now, I will demonstrate everything that you need to do on this part.
   if Key == LeftShiftKey then
      KeysDown.LShift = false -- upon release we will set the buffer values false
   elseif Key == LeftControlKey then
      KeysDown.LCtrl = false -- see above comment
   end
end

local function UpdateThrottleValue(Deltatime)
   ThrottleDirection = (KeysDown.LShift and 1 or 0) + (KeysDown.LCtrl and -1 or 0)
   local Current = ThrottleValue; -- hold our current state
   local Goal = Current + (ThrottleIncPerSecond * ThrottleDirection)
   ThrottleValue = math.clamp((1-Deltatime) * Current + (Deltatime * Goal), 0, MaxThrottle)
   -- Let me explain the above line.
   -- The formula here is 1 - Alphatime * Start + Alphatime * Goal
   -- Also known as, linear interpolation! (lerp)
   -- Alphatime is an arbitrary term for: It can be anything from range0 to range1; 0-1 in this case
   -- inline function: function(x0,x1,a) return (1-a)*x0+(a*x1) end
   -- Anyways, this could've been simplified, but I'm doing this for learning purposes.
   -- I hope this has taught you something, if not; sad boy hours I suppose.
   -- QuickFIX: Adding attribute update code
   ThatSecondParent:SetAttribute("currentThrottle", ThrottleValue)
end
RunService.Heartbeat:Connect(UpdateThrottleValue)
local InputBegan = uInputService.InputBegan:Connect(HandleKeyPressed)
local InputEnded = uInputService.InputEnded:Connect(HandleKeyReleased)
-- Oh right, and finally we connect the update function to Heartbeat; and connect our input handlers!

-- EDIT: ThrottleValue is the equivalent variable to currentThrottle
-- QuickFIX: Forgot to clamp the value! Fixed that, MaxThrottle is the correspondent max value!
2 Likes

I tried that but on line 59 it gives me this,


any help? Oh yeah, side note, I changed the “CurrentThrottle” near the end to my variable “currentThrottle” because it was giving me an error. Also, when I was showing you my current code, it was a bit different than the one you showed, and I did read the comment.

1 Like

Sorry this was an error on my part, give it a second to update and I’ll have it fixed (with the original variable formatting)

I forgot to define CurrentThrottle :stuck_out_tongue:
EDIT: I forgot I renamed CurrentThrottle

All issues have been fixed. The code should operate as intended now.

EDIT: Also, I may have come off a little rude on my lecture session, but I was tired when I first drafted that reply. My apologies.

Maybe I overlooked something, but where are the functions HandleKeyPressed and HandleKeyReleased called? Is there more to the script that isn’t shown?

1 Like

Oh my god. I must be going senile. Thank you.
Yes, I forgot the UserInputService connections; holy.

EDIT: This is now fixed. The code should be 100% functional now; although I have not tested it.

1 Like

Alright, thanks so much! Works like a dream.

1 Like

Okay, so there’s a problem I ran into a problem… (before I go any further I just want to state this is in a local script but you probably already know this) I added a print that shows all the throttle values and it seems to be adding up, and for the velocity, I made a different script that does this:

local throttle = script.Parent.Parent:GetAttribute("throttleInput")
local currentThrottle = script.Parent.Parent:GetAttribute("currentThrottle")

while wait() do
	script.Parent.Parent.BodyVelocity.Velocity = script.Parent.Parent.CFrame.LookVector * currentThrottle
end

but, It doesn’t seem to be working…

You’re only getting the currentThrottle value once; and my script didn’t update it.

I’ll edit my script to update the attribute, and you should use

ObjectWithAttribute:GetAttributeChangedSignal("currentThrottle"):Connect(function()
   ObjectWithAttribute.BodyVelocity.Velocity = ObjectWithAttribute.CFrame.LookVector * ObjectWithAttribute:GetAttribute("currentThrottle")
end)

Note: Remove your wait() loop. Do NOT put this inside of it. Just replace the loop with this.
Also do not forget to change ObjectWithAttribute to your reference.

And again, you absolutely need to get into the flow of defining variables references, not writing them out all the time.
It does the following:

  1. Saves time writing out strict paths
  2. Has better performance (less-so with the addition of the LuaU VM & it’s optimizations, but still.
  3. Good practice, and absolutely required for neat organization.
  4. Extending [3]; You can accurately name reference variables instead of just blind .Parent repition.
  5. It makes it much easier for us to assist you, because we’re not immediately drawn away by your code. There are helpful people on this forum, but they may take a while to get to you.
    Any of the other people who skim your post and the replies, may be deterred by it.

Although this is just constructive criticism, please do not take it harshly.
I’m not going to turn because you haven’t started; but please consider it.

image

local throttle = script.Parent.Parent:GetAttribute("throttleInput")
local currentThrottle = script.Parent.Parent:GetAttribute("currentThrottle")

local ObjectWithAttribute = script.Parent.Parent

ObjectWithAttribute:GetAttributeChangedSignal("currentThrottle"):Connect(function()
	ObjectWithAttribute.BodyVelocity.Velocity = ObjectWithAttribute.CFrame.LookVector * ObjectWithAttribute:GetAttribute("currentThrottle")
end)

Doesn’t seem to be working…

(Oh, and that picture is while the playtest is running by the way.)

Oh yeah, and when I look at the attribute in the properties tab, the value doesn’t change but when I print it out in the output it does.

Sorry I had to leave for a moment, I didn’t edit my code; let me add that to it real quick.

This code is now updated; it will:

  • Respond to user input
  • Update according attributes for user input
1 Like

I tried that but it doesn’t seem to be working…

The attributes still don’t change as well, I’ve tried some different things that don’t give errors and its still acting weird.

Sorry, I won’t be able to assist further than this anymore; dealing with some personal matters.

I’m also not exactly sure what’s going wrong. I can’t really help much further than this.

Sorry that I couldn’t solve your issue.

Regards,
IDoLua

truth is, attributes suck

(in this situation don’t use them, it is what broke it.)