Forest Logo
search
package_2

dispense-prop

By @khanpython

Roblox

Mirrored

Prop Dispenser

System for physically dispensing and attracting in-game props towards a target.

[image: Prop Dispenser Demo]

Features:

  • Customizable Prop Spawning: Spawn a specified number of props from a given origin with configurable settings.
  • Automatic Prop Removal: Optionally remove props automatically after a configurable duration.
  • Sigmoid Attraction: Props are attracted towards a target using a smooth sigmoid easing curve over a configurable duration.
  • Dynamic Timing: AttractDelay and AttractDuration accept a fixed number, a Vector2 range for per-prop randomization, or a custom function.
  • Batch Movement: All attracted props are moved in a single workspace:BulkMoveTo call each frame for minimal overhead.
  • SoA Data Layout: Internal state uses flat, cache-friendly Structure of Arrays instead of nested dictionaries.
  • Event Callbacks: Attach custom logic to prop spawn, removal, and group completion events.

Installation via Wally:

  1. Ensure you have the Wally package manager installed on your system.
  2. Add the following line to your wally.toml file under the [dependencies] section:
    dispense-prop = "khanpython/dispense-prop@5.1.0"
    
  3. Run the Wally install command to download and integrate the package:
    wally install
    
  4. The package will be placed in your Packages folder. Use the following code snippet to require it in your project:
    local PropManager = require(path-to-package)
    

Parameters:

PropManager:Start(amount, originCFrame, propTemplate, targetInstance, settings)

ParameterTypeDescription
amountnumberThe number of props to spawn. Must be an integer >= 1.
originCFrameCFrameThe starting position for the props.
propTemplateModel or BasePartA template instance to clone for each prop.
targetInstanceModel or BasePartThe target instance towards which props may be attracted.
settingstableA table of configuration options for the props. See details below.

settings Table

DynamicNumber = number | Vector2 | (propIndex: number) -> number

KeyTypeDefaultDescription
RemoveMagnitudenumber?1The distance within which a prop is removed (claimed).
AttractMagnitudenumber?nilThe range within which props begin attraction. nil means attract from any distance.
AttractDurationDynamicNumber?1.5How long (seconds) the sigmoid attraction takes to complete.
AttractDelayDynamicNumber?3Time delay before a prop becomes live and can be attracted/claimed.
AutoRemoveTimenumber?nilTime in seconds before props are automatically removed.
CollisionGroupstring?PropsThe collision group assigned to props.
OnSpawnfunctionrequiredA function executed when a prop is spawned.
OnRemovedfunction?nilA function executed when a prop is removed. Receives true if forced (auto-remove/external destroy).
OnAllRemovedfunction?nilA function executed when all props in a group are removed.

DynamicNumber

AttractDelay and AttractDuration accept a DynamicNumber, which resolves per-prop at spawn time. This lets you stagger or randomize timing across a group.

FormBehaviorExample
numberSame value for every propAttractDelay = 2
Vector2Random value in [min, max] (order doesn't matter)AttractDuration = Vector2.new(1, 3)
functionCalled with the prop's 1-based index in the groupAttractDelay = function(i) return i * 0.15 end
-- Fixed: all props attract after 2 seconds
AttractDelay = 2

-- Random: each prop gets a random delay between 1 and 4 seconds
AttractDelay = Vector2.new(1, 4)

-- Staggered: prop 1 waits 0.15s, prop 2 waits 0.3s, etc.
AttractDelay = function(propIndex)
    return propIndex * 0.15
end

Example Usage:

local PropDispenser = require(path-to-package)

local settings = {
    RemoveMagnitude = 2,
    AttractMagnitude = 20,
    AttractDuration = Vector2.new(1, 2), -- random 1-2s per prop
    AttractDelay = 3,
    AutoRemoveTime = 30,
    CollisionGroup = "Props",

    --! Important: Avoid yielding any of the callbacks. `OnSpawn` could be an exception, though it may lead to unexpected results.
    OnSpawn = function(prop)
        print("Spawned prop:", prop)

        -- You must manually parent the prop
        prop.Parent = workspace

        local randomX = math.random(-10, 10)
        local randomY = math.random(5, 10)
        local randomZ = math.random(-10, 10)

        -- Your `spill` logic
        task.defer(function()
            prop.PrimaryPart:ApplyImpulse(
                Vector3.new(randomX, randomY, randomZ)
            )
        end)
    end,
    OnRemoved = function(wasForced)
        print("Prop removed.")

        --[[
            * Triggered whenever a single prop is removed.
            * `wasForced` is `true` if the removal was due to auto-remove, external destruction,
              or the target leaving. It is `nil`/falsy when the prop was claimed normally.
        ]]
    end,
    OnAllRemoved = function()
        print("All props removed.")

        --[[
            * Triggered when every prop in the group has been removed,
              regardless of how each individual removal occurred.
        ]]
    end,
}

PropDispenser:Start(
    10, -- Number of props
    CFrame.new(0, 10, 0), -- Origin position
    workspace.PropTemplate, -- Template prop instance
    workspace.Target, -- Target instance
    settings -- Settings table
)

Package Details

Install command (Click to copy)


Version

5.1.0

License

MIT

check_circle

Safe for commercial use

Automated license review — not legal advice.