luaroblox

In Lua Roblox API, how does one debounce a player/character that runs into a box?


Background:

Bear minimal objects. Just a player and a part (rectangular prism) that's floating in the air and anchored.

I honestly don't understand what is happening under the hood so its hard to figure out. Without debounce, upon event firing, the callback function is invoked by connect() or the event handler (not sure), and, without debouncing, the function is invoked multiple times as print statements are repeated on the output box. So, with a variable (of type boolean) that stores a debounce flag, that solves it. I then try to "un-debounce" when the player's model is outside out of the box's model. But, I don't know how to do that properly.

Here's my code attempt:

local box = game.Workspace.BoxModel;
local debounce = false;

local function onTouchedDebounced()
    if (debounce == false)
    then
        debounce = true;
        print("Hello! onTouchedDebounced() has run.");
        box.Touched:Connect(onTouchedDebounced));
    end
end

local function onTouchedUndebounced()
    if (debounce == true) 
    then
        print("Hello! onTouchedUndebounced() has run.");
        debounce = false;
    end 
end

box.Touched:Connect(onTouchedDebounced);
box.TouchEnded:Connect(onTouchedUndebounced);

Solution

  • The heart of what you're doing is sound : start blocking after the first event, and unblock some time later. If this were with button presses or mouse-clicks, your solution would work fine. This is complicated by the Touched event, as it fires with any part that touches it, and a player's character could have multiple touch points.

    The Touched and TouchEndeded events give you a reference to the instance that has touched the part.

    If the goal is to only fire the box event once per player or once while anyone is touching it, you can keep a dictionary of parts that are currently touching the box. When a part touches, you increment a counter. When a part stops, you decrement it. You only remove the debounced flag once all touch points have been removed.

    local Players = game:GetService("Players")
    
    local box = game.Workspace.BoxModel
    local playersTouching = {} --<string playerName, int totalParts>
    
    local function onTouched(otherPart)
        -- check that the thing that touched is a player
        local player = Players:GetPlayerFromCharacter(otherPart.Parent)
        if not player then
            warn(otherPart.Name .. " isn't a child of a character. Exiting")
            return
        end
    
        -- check whether this player is already touching the box
        local playerName = player.Name
        local total = playersTouching[playerName]
        if total and total > 0 then
            warn(playerName .. " is already touching the box")
            playersTouching[playerName] = total + 1
            return
        end
    
        -- handle a new player touching the box
        playersTouching[playerName] = 1
    
        -- Do a thing here that you only want to happen once per event...
        print(string.format("%s has begun touching", playerName))    
    end
    
    local function onTouchEnded(otherPart)
        -- decrement the counter for this player
        local playerName = otherPart.Parent.Name
        if playersTouching[playerName] == nil then
            return
        end
    
        local newTotal = playersTouching[playerName] - 1
        playersTouching[playerName] = newTotal
    
        -- if the total is back down to zero, clear the debounce flag
        if newTotal == 0 then
            playersTouching[playerName] = nil
            print(string.format("%s has stopped touching", playerName))
        end
    end
    
    
    box.Touched:Connect(onTouched)
    box.TouchEnded:Connect(onTouchEnded)