Better Loops - Module to handle loops in a intelligent way

BetterLoops Module

Overview

The BetterLoops module provides a way to manage and execute callbacks at regular intervals, with support for distance-based frame interval adjustments. Callbacks can be added with optional parameters to customize their behavior, and they can be removed using a unique identifier.

Functions

bindCallback(callback, player, target, framesInterval, minDistance, maxDistance, params)

Adds a callback to be executed periodically.

  • callback: Function - The function to be executed.
  • player: Instance (optional) - A Player instance used for distance-based callback execution.
  • target: Instance (optional) - A BasePart instance representing the target for distance measurement.
  • framesInterval: Number - The number of frames between callback executions (defaults to 1).
  • minDistance: Number - The minimum distance for adjusting the frame interval (defaults to 1).
  • maxDistance: Number (optional) - The maximum distance to limit frame interval adjustments.
  • params: Table (optional) - Additional parameters to pass to the callback.

Returns: Number - A unique identifier for the added callback.

unbindCallback(callbackId)

Removes a callback using its unique identifier.

  • callbackId: Number - The unique identifier of the callback to be removed.

Returns: Boolean - true if the callback was successfully removed, otherwise false.

Example Usage

Adding a Callback

local BetterLoops = require(game.ServerScriptService.BetterLoops)

-- Define a callback function
local function myCallback(param1, param2)
    print("Callback executed with params:", param1, param2)
end

-- Add the callback with parameters
local callbackId = BetterLoops.bindCallback(
    myCallback,
    game.Players.LocalPlayer, -- Player instance (optional for distance-based callback)
    workspace.TargetPart,     -- Target instance (optional for distance-based callback)
    10,                       -- Frames interval
    5,                        -- Minimum distance
    50,                       -- Maximum distance
    { "Hello", "World" }      -- Parameters to pass to the callback
)

Removing a Callback

-- Remove the callback using its unique identifier
local success = BetterLoops.unbindCallback(callbackId)

if success then
    print("Callback successfully removed.")
else
    print("Callback removal failed.")
end

The module

-- BetterLoops.lua

local RunService = game:GetService("RunService")

local BetterLoops = {}
local callbacks = {}
local nextId = 1

-- Adds a callback with configuration parameters and error handling
-- @param callback: Function to be executed
-- @param player: (Optional) Player instance for distance-based callback
-- @param target: (Optional) Target instance (BasePart) for distance-based callback
-- @param framesInterval: Number of frames between callback executions
-- @param minDistance: Minimum distance for distance-based frame interval adjustment
-- @param maxDistance: Maximum distance for distance-based frame interval adjustment
-- @param params: Additional parameters to be passed to the callback
-- @return: Unique identifier for the callback
function BetterLoops.bindCallback(callback, player, target, framesInterval, minDistance, maxDistance, params)
    assert(type(callback) == "function", "The 'callback' parameter must be a function.")
    
    local useDistance = player and target
    local playerInstance = useDistance and player
    local targetInstance = useDistance and target
    
    if useDistance then
        assert(typeof(playerInstance) == "Instance" and playerInstance:IsA("Player"), "The 'player' parameter must be an instance of type Player.")
        assert(typeof(targetInstance) == "Instance" and targetInstance:IsA("BasePart"), "The 'target' parameter must be an instance of type BasePart.")
    end
    
    framesInterval = framesInterval or 1
    assert(framesInterval > 0, "The 'framesInterval' parameter must be a positive value.")
    
    minDistance = minDistance or 1
    
    local callbackId = nextId
    nextId = nextId + 1

    local newCallback = {
        id = callbackId,
        callback = callback,
        framesInterval = framesInterval,
        currentFrame = 0,
        useDistance = useDistance,
        player = playerInstance,
        target = targetInstance,
        minDistance = minDistance,
        maxDistance = maxDistance,
        params = params or {} -- Stores additional parameters
    }
    
    table.insert(callbacks, newCallback)
    return callbackId
end

-- Removes a callback by its unique identifier
-- @param callbackId: Unique identifier of the callback to be removed
-- @return: Boolean indicating if the callback was successfully removed
function BetterLoops.unbindCallback(callbackId)
    for i = #callbacks, 1, -1 do
        if callbacks[i].id == callbackId then
            table.remove(callbacks, i)
            return true
        end
    end
    return false
end

-- Internal function optimized for executing callbacks
local function onRenderStep()
    local numCallbacks = #callbacks
    for i = 1, numCallbacks do
        local data = callbacks[i]
        
        if data.useDistance then
            local character = data.player.Character
            local primaryPart = character and character.PrimaryPart
            local targetPosition = data.target and data.target.Position
            
            if primaryPart and targetPosition then
                local playerPosition = primaryPart.Position
                local distance = (playerPosition - targetPosition).Magnitude
                
                -- Update frame interval based on distance
                data.framesInterval = math.max(math.floor(distance), data.minDistance)
                
                -- Ensure maxDistance is valid and within range
                if data.maxDistance and distance > data.maxDistance then
                    data.framesInterval = data.minDistance
                end
            end
        end

        data.currentFrame = data.currentFrame + 1
        if data.currentFrame >= data.framesInterval then
            local success, err = pcall(function()
                data.callback(table.unpack(data.params)) -- Passes parameters to the callback
            end)
            if not success then
                warn("Callback execution error: " .. tostring(err))
            end
            data.currentFrame = 0
        end
    end
end

-- Connect the RenderStepped event to the onRenderStep function
RunService.RenderStepped:Connect(onRenderStep)

return BetterLoops
5 Likes

If your find any problem with the module please tell me.

2 Likes