Debounce: Difference between revisions

From Legacy Roblox Wiki
Jump to navigationJump to search
>SoulStealer9875
No edit summary
>NXTBoy
Hopefully tidied up a bit
Line 1: Line 1:
==Debounce==
A '''Debounce''' system is a set of code that keeps a function from running too many times. It comes from the idea of mechanical switch bounce, where a switch bounces when pushed, creating multiple signals. In the context of Roblox, this problem occurs mainly with the [[Touched]] event, when a part touches another multiple times in a short space of time.
===What is Debounce?===
==Theory==
A '''Debounce''' system is a set of code that keeps a function from running too many times. It comes from the idea of someone bouncing against a wall and triggering an event many times. You don't always want your code to run again and again, so Debounces are used to keep this from happening.
 
===Theory===
Let's say you have a button on the floor. When you jump on the button, it prints a message to the output. Your code would look like this:
Let's say you have a button on the floor. When you jump on the button, it prints a message to the output. Your code would look like this:


{{Example|<pre>
<pre>
function OnTouch(hit) --Creating the function
game.Workspace.Button.Touched:connect(function(hit)
print ("Button pressed") --Print the message
    print("Button pressed") --Print the message
wait(1) --Wait for 1 second
    wait(1)                 --Wait for 1 second
print ("Hi :D") --Print another message
    print("Hi :D")         --Print another message
end --Tells the function where to stop
end)
 
</pre>
game.Workspace.Button.Touched:connect(OnTouch) --Connect the Event to the Function
</pre>}}


This will put this message in the output:
This will put this message in the output:
Line 34: Line 29:
Hi :D
Hi :D
</pre>
</pre>
Rather than executing sequentially, all the event handlers execute at the same time.


Now if you're using a button for a [[How to Make a Model Regenerate|Regeneration Script]] then it will make 5 of whatever you are regenerating. This is an issue because ''all 5 will be in the same spot'' causing all kinds of problems. So you can see the problem when you have a script without Debounce.
Now if you're using a button for a [[How to Make a Model Regenerate|Regeneration Script]] then it will make 5 of whatever you are regenerating. This is an issue because ''all 5 will be in the same spot'' causing all kinds of problems. So you can see the problem when you have a script without a debounce system.


Debounce is a system that prevents this kind of problem. When an action happens, such as someone pressing your floor button, the script '''locks''' any new actions from that event until a time passes or the action is complete. It can be used for much more than just buttons, think of different ways you can use it for your scripts.
Debouncing a system prevents this kind of problem. When an action happens, such as someone pressing your floor button, the script '''locks''' any new actions from that event until a time passes or the action is complete. It can be used for much more than just buttons, think of different ways you can use it for your scripts.


===Use Case===
==Use Case==
It's fairly simple to convert an existing script to using debounce. Lets use the same script we had above, and add a couple of lines. In this case we will put in a time limit to wait for until the function can be run again.
It's fairly simple to convert an existing script to using debounce. Lets use the same script we had above, and add a couple of lines. In this case we will put in a time limit to wait for until the function can be run again.


{{Example|<pre>
{{Example|<pre>
enabled = true
local buttonPressed = false
--Store whether the button is pressed in a local variable
 
game.Workspace.Button.Touched:connect(function(hit)
    if not buttonPressed then
    -- Is it not pressed?
 
        buttonPressed = true
        -- Mark it as pressed, so that other handlers don't execute
 
        print("Button pressed")
        wait(1)
        print("Hi :D")
        -- Do Stuff


function onTouch(hit)
        buttonPressed = false
    if not enabled then return end
        -- Mark it as not pressed, so other handlers can execute again
    enabled = false
     end
    print("Button pressed")
end)
     wait(1)
    print("Hi :D")
    enabled = true
end
game.Workspace.Button.Touched:connect(OnTouch)
</pre>}}
</pre>}}
#The first line that we added was <code style="color:blue">enabled = true</code>. This line creates a [[Variables|global variable]] called '''enabled'''. This is the ''flag'' that we will use to let the function know if it is allowed to run or not.
#The second line <code style="color:blue">if not enabled then return end</code> is what's called a '''check'''. It '''checks''' to see if the script is allowed to run. <code style="color:blue">if</code> the function is <code style="color:blue">not</code> true (<code style="color:blue">enabled</code>) <code style="color:blue">then return</code> nothing and <code style="color:blue">end</code>.
#The third line <code style="color:blue">enabled = false</code> sets the variable enabled to <code style="color:blue">false</code>. This ''flags'' the script, letting it know not to run. This is how the check works.
#The fourth line <code style="color:blue">enabled = true</code> tells the script at the end of the function that it is ok to run the function again, because the function has ended. The ''flag'' is set to go.


This will cause your output to look like this:
This will cause your output to look like this:
Line 69: Line 68:
That's more like it! You can use this same concept, by adding the same 4 lines to different scripts, in most any script involving functions. It doesn't even have to just be touched objects, it can be used to keep people from pressing a button more than once, firing a weapon more often than you want, or preventing a new event from happening before the old one is done. Take a look at the next example.
That's more like it! You can use this same concept, by adding the same 4 lines to different scripts, in most any script involving functions. It doesn't even have to just be touched objects, it can be used to keep people from pressing a button more than once, firing a weapon more often than you want, or preventing a new event from happening before the old one is done. Take a look at the next example.


===Real World===
==Real World==


Here's the Local Gui script of the Rocket Launcher tool:
Here's the Local Gui script of the Rocket Launcher tool:


{{Example|<pre>
<pre>
enabled = true
enabled = true
function onButton1Down(mouse)
function onButton1Down(mouse)
if not enabled then
    if not enabled then
return
        return
end
    end


enabled = false
    enabled = false
mouse.Icon = "rbxasset://textures\\GunWaitCursor.png"
    mouse.Icon = "rbxasset://textures\\GunWaitCursor.png"


wait(12)
    wait(12)
mouse.Icon = "rbxasset://textures\\GunCursor.png"
    mouse.Icon = "rbxasset://textures\\GunCursor.png"
enabled = true
    enabled = true


end
end
</pre>}}
</pre>


When you fire a rocket, the script shows the reload icon. Then the function waits for 12 seconds. During this time, enabled is false, so if the player tries to fire another rocket, the script won't run because the function will just return right away. After the 12 seconds are up, the reload cursor goes away and enabled becomes true again, allowing the user to fire another rocket.
When you fire a rocket, the script shows the reload icon. Then the function waits for 12 seconds. During this time, enabled is false, so if the player tries to fire another rocket, the script won't run because the function will just return right away. After the 12 seconds are up, the reload cursor goes away and enabled becomes true again, allowing the user to fire another rocket.
==Advanced notation==
After a while, it might get tedious defining a separate debounce variable for each event handler. Instead, you can write a debounce function, that returns a debounced copy of it's first argument.
<pre>
function debounce(func)
    local isRunning = false
    return function(...)
        if not isRunning then
            isRunning = true
            func(...)
            isRunning = false
        end
    end
end
</pre>
Applying this to the original code:
<pre>
game.Workspace.Button.Touched:connect(debounce(function(hit)
    print("Button pressed") --Print the message
    wait(1)                --Wait for 1 second
    print("Hi :D")          --Print another message
end))
</pre>


[[Category:Scripting Tutorials]]
[[Category:Scripting Tutorials]]

Revision as of 14:24, 12 July 2011

A Debounce system is a set of code that keeps a function from running too many times. It comes from the idea of mechanical switch bounce, where a switch bounces when pushed, creating multiple signals. In the context of Roblox, this problem occurs mainly with the Touched event, when a part touches another multiple times in a short space of time.

Theory

Let's say you have a button on the floor. When you jump on the button, it prints a message to the output. Your code would look like this:

game.Workspace.Button.Touched:connect(function(hit)
    print("Button pressed") --Print the message
    wait(1)                 --Wait for 1 second
    print("Hi :D")          --Print another message
end)

This will put this message in the output:

Button pressed
Hi :D

The problem is that the physics engine, because of the way bricks touch each other, will not register just one collision, it may cause several Touched events to fire. So your output will look more like this:

Button pressed
Button pressed
Button pressed
Button pressed
Button pressed
Hi :D
Hi :D
Hi :D
Hi :D
Hi :D

Rather than executing sequentially, all the event handlers execute at the same time.

Now if you're using a button for a Regeneration Script then it will make 5 of whatever you are regenerating. This is an issue because all 5 will be in the same spot causing all kinds of problems. So you can see the problem when you have a script without a debounce system.

Debouncing a system prevents this kind of problem. When an action happens, such as someone pressing your floor button, the script locks any new actions from that event until a time passes or the action is complete. It can be used for much more than just buttons, think of different ways you can use it for your scripts.

Use Case

It's fairly simple to convert an existing script to using debounce. Lets use the same script we had above, and add a couple of lines. In this case we will put in a time limit to wait for until the function can be run again.

Example
local buttonPressed = false
--Store whether the button is pressed in a local variable

game.Workspace.Button.Touched:connect(function(hit)
    if not buttonPressed then
    -- Is it not pressed?

        buttonPressed = true
        -- Mark it as pressed, so that other handlers don't execute

        print("Button pressed")
        wait(1)
        print("Hi :D")
        -- Do Stuff

        buttonPressed = false
        -- Mark it as not pressed, so other handlers can execute again
    end
end)


This will cause your output to look like this:

Button pressed
Hi :D

That's more like it! You can use this same concept, by adding the same 4 lines to different scripts, in most any script involving functions. It doesn't even have to just be touched objects, it can be used to keep people from pressing a button more than once, firing a weapon more often than you want, or preventing a new event from happening before the old one is done. Take a look at the next example.

Real World

Here's the Local Gui script of the Rocket Launcher tool:

enabled = true
function onButton1Down(mouse)
    if not enabled then
        return
    end

    enabled = false
    mouse.Icon = "rbxasset://textures\\GunWaitCursor.png"

    wait(12)
    mouse.Icon = "rbxasset://textures\\GunCursor.png"
    enabled = true

end

When you fire a rocket, the script shows the reload icon. Then the function waits for 12 seconds. During this time, enabled is false, so if the player tries to fire another rocket, the script won't run because the function will just return right away. After the 12 seconds are up, the reload cursor goes away and enabled becomes true again, allowing the user to fire another rocket.

Advanced notation

After a while, it might get tedious defining a separate debounce variable for each event handler. Instead, you can write a debounce function, that returns a debounced copy of it's first argument.

function debounce(func)
    local isRunning = false
    return function(...)
        if not isRunning then
            isRunning = true
            func(...)
            isRunning = false
        end
    end
end

Applying this to the original code:

game.Workspace.Button.Touched:connect(debounce(function(hit)
    print("Button pressed") --Print the message
    wait(1)                 --Wait for 1 second
    print("Hi :D")          --Print another message
end))