Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- [ Blackhole 5.0 Alpha Release 4 ] --
- -- Integrated smoother BodyForce-based graviaty for stable simulations.
- -- Archived versions - visit my pastebin: https://pastebin.com/u/1m1m0
- local hole = script.Parent -- Quick Docs, more at `Documentation`
- local gravitationalConstant = 1000 -- Adjust this value to control the strength of the attraction [1000 is default for a black hole]
- local updateInterval = 0 -- Time in seconds between updates while computing (Higher = +Accuracy -Performance, Lower = -Accuracy +Performance)
- local damping = 0.7 -- Adjust this value to control the rate of force application [1 is no damping, 0.7 is a suggested starting point]
- local rangeFactor = 500 -- Maximum range for objects to be effected [500 studs is default]
- local transparencySteps = {0.25, 0.5, 0.75} -- Transparency changes. (Appearing to fade out of reality)
- local dematerializeDuration = 0.01 -- How long (seconds) the fading will last (In seconds) on each transparency increment
- local sizeProportion = 42 -- Smaller = faster growth for each absorption !! CANNOT BE 0 (ZERO) !!
- local rangeProportion = 125 -- Bigger = faster range growth
- local partColor = BrickColor.new(0) -- Color it changes into before getting deleted. (White is default)
- local partMaterialTransform = Enum.Material.Neon -- Material it changes into before getting deleted. (Neon is default)
- local absorption = true -- Absorb objects that touch the blackhole
- local growth = true -- Allow the blackhole to grow by absorption
- local killPlayers = true -- Kill players that touch the blackhole !(disable when merging a lot of blackholes!)!
- local collision = false -- Make objects pass through eachother while being absorbed
- local fixedPosition = true -- Anchor objects while being absorbed (Better FPS)
- local toggleMicroGravity = false -- This script works best in zero gravity environments! (Optional)
- -- Beta Parameters -- Latest Feature Additions Preview
- local infinityHandler = true -- Prevents objects from reaching infinite velocity
- local paradoxHandler = false -- Prevents two or more blackholes from absorbing eachother when touching
- local mergeBlackholes = true -- Merge blackholes based on size
- local gravityEngine = "bodyforce" -- Switch between BodyForce and Velocity-based gravitational attraction (1 or 2 if youre lazy)
- local glint = hole.VFX.Glint -- Particle emitter, must be changed when path or name is also changed
- local glintProportion = 1.5 -- Factor on glint growing proportionally to the blackhole for realism (multiplication. to divide, use fractions)
- local rocheLimitFactor = 3 -- Multiplier for the black hole's radius to define the Roche limit (area of deconstruction on welds and joints)
- local gravitySync = true -- Toggle to syncrhonize velocity-based gravity with force-based. Velocity-based gravity is violent!
- -- GRAVITATIONAL FORCES --
- local lastUpdateTime = 0
- local CollectionService = game:GetService("CollectionService")
- local previousSize = hole.Size.X
- local glintProportions = previousSize * glintProportion
- -- Logger and error handler [WIP]
- local logger = {
- bodyForce = false,
- velocity = false,
- sizeProp = false,
- zeroGrav = false,
- consuming = false,
- gravEngine = false
- }
- -- Roche Limit, or in normal person terms, the point where objects decontruct due to its proximity
- local function DestroyJointsWithinRocheLimit(object)
- local direction = hole.Position - object.Position
- local distance = direction.Magnitude
- local rocheLimit = (hole.Size.Magnitude / 2) * rocheLimitFactor -- rocheLimitFactor adjusts the Roche limit size
- -- Check if the object is within the Roche limit
- if distance <= rocheLimit then
- -- Iterate through all descendants of the object
- for _, descendant in pairs(object:GetDescendants()) do
- -- Check if the descendant is a JointInstance and destroy it
- if descendant:IsA("JointInstance") then
- descendant:Destroy()
- end
- end
- end
- end
- -- Prevent division by zero
- if sizeProportion == 0 then
- sizeProportion = 10e-4
- if not logger.sizeProp then
- print("{BHv5A} sizeProportion cannot be 0! (division by zero)")
- end
- end
- local function ApplyGravity(object)
- -- Delay to pause calculations for better FPS
- local currentTime = tick()
- if currentTime - lastUpdateTime < updateInterval then return end
- lastUpdateTime = currentTime
- -- Main bulk of gravitational forces
- local direction = hole.Position - object.Position
- local distance = direction.Magnitude
- local holeRadius = hole.Size.Magnitude / 2 -- [NEW!] Make the radius of infinityHandler bigger or smaller (default, /2) something epic will happen ;)
- -- -------Don't touch, this formula has been tweaked for exact range of effectiveness!-------- --
- local distanceSquared = (distance / ((rangeFactor / 100) + ((rangeFactor / 100) * (1 / 64)))) ^ 2
- -- ------------------------------------------------------------------------------------------- --
- local magnitude = gravitationalConstant / distanceSquared
- -- Adjust gravitational constant for velocity-based gravity
- local engineType = string.lower(gravityEngine)
- if engineType == "velocity" or engineType == "speed" or engineType == "2" and gravitySync then
- magnitude = magnitude / (gravitationalConstant * 0.1) -- Dampening factor for velocity-based gravity
- end
- local force = direction.Unit * magnitude * object:GetMass()
- -- [NEW!] --
- -- Stops objects from speeding to infinity at the center of the hole
- if infinityHandler then
- if distance < holeRadius then
- -- Force adjustment for both engines
- force = Vector3.zero -- Removes gravity, acts like a vacuum (make into -force for something epic to happen)
- direction = Vector3.zero -- Prevents direction inversion for velocity-based logic
- end
- end
- -- Apply the gravity engine logic based on selected type
- if engineType == "bodyforce" or engineType == "force" or engineType == "1" then
- local bodyForce = object:FindFirstChild("BlackHoleBodyForce")
- if not bodyForce then
- bodyForce = Instance.new("BodyForce")
- bodyForce.Name = "BlackHoleBodyForce"
- bodyForce.Force = Vector3.new(0, 0, 0)
- bodyForce.Parent = object
- end
- -- Apply damping to the force to prevent instability
- bodyForce.Force = (bodyForce.Force + force) * damping
- -- Print the gravity type only once
- if not logger.bodyForce then
- print("{BHv5A} Using FORCE-based gravity (+Stability, -Accuracy) MODEL SUPPORTED")
- logger.bodyForce = true
- end
- elseif engineType == "velocity" or engineType == "speed" or engineType == "2" then
- -- Apply custom velocity-based gravity
- local blackHoleRadius = hole.Size.X / 2
- -- Prevents objects from accelerating too fast or getting stuck at the black hole center
- if distance < blackHoleRadius and infinityHandler then
- -- Override velocity adjustments inside the radius
- return
- elseif distance < blackHoleRadius then
- -- If infinityHandler is not enabled, ignore object adjustments inside the radius
- return
- end
- -- Apply the custom velocity-based gravity formula
- object.Velocity = object.Velocity + force * damping
- -- Print the gravity type only once
- if not logger.velocity then
- print("{BHv5A} Using VELOCITY-based gravity (-Stability, +Accuracy) DOES NOT SUPPORT MODELS!!!")
- logger.velocity = true
- end
- elseif not logger.gravEngine then
- -- Print an error for invalid gravity engine types
- print("{BHv5A} Invalid Gravity Engine request! Switching to default (Force-based Gravity)")
- logger.gravEngine = true
- gravityEngine = "1"
- end
- end
- -- MAIN GRAVITATIONAL FORCE COMPUTER (Applies gravity to anything) --
- local function CheckAndApplyGravity(obj)
- -- Check if the object is a descendant of a BasePart (including all physical objects)
- if not obj:IsDescendantOf(game.Workspace) or not obj:IsA("BasePart") then
- return
- end
- -- Exclude the black hole itself and players from gravitational pull
- if obj == hole or obj.Parent:IsA("Player") then -- Remove obj.Parent:IsA("Player") if you like
- return
- end
- -- Exclude anchored objects to maximize performance
- if obj.Anchored then
- return
- end
- -- Check for Roche limit and destroy joints if necessary
- DestroyJointsWithinRocheLimit(obj)
- ApplyGravity(obj)
- end
- game:GetService("RunService").Heartbeat:Connect(function()
- for _, object in pairs(game.Workspace:GetDescendants()) do
- CheckAndApplyGravity(object)
- end
- end)
- -- FUNCTION TO ABSORB SMALLER BLACK HOLES [BETA] --
- local function AbsorbSmallerBlackHole(otherBlackHole)
- -- Helper function to calculate the average size of a black hole
- local function AverageSize(part)
- return (part.Size.X + part.Size.Y + part.Size.Z) / 3
- end
- -- Get sizes of both black holes and calculate the size difference
- local thisSize = AverageSize(hole)
- local otherSize = AverageSize(otherBlackHole)
- local sizeDifference = math.floor((thisSize - otherSize) * 1000 + 0.5) / 1000
- local function resetForces(blackHole)
- local bodyForce = blackHole:FindFirstChild("BlackHoleBodyForce")
- if bodyForce then
- bodyForce.Force = Vector3.new(0, 0, 0)
- end
- end
- local function applyNewForce(blackHole)
- local newForce = blackHole.Size.Magnitude * 2
- local newBodyForce = Instance.new("BodyForce")
- newBodyForce.Force = Vector3.new(0, newForce, 0)
- newBodyForce.Parent = blackHole
- end
- if sizeDifference > 0.001 then
- -- Larger black hole absorbs the smaller one
- CollectionService:RemoveTag(otherBlackHole, "Blackholev5")
- task.wait(0.01)
- resetForces(otherBlackHole)
- -- Increase size and range of the larger black hole
- hole.Size = otherBlackHole.Size * 0.5
- rangeFactor = (hole.Size.Magnitude * 0.5) * (rangeProportion / sizeProportion)
- otherBlackHole:Destroy()
- resetForces(hole)
- applyNewForce(hole)
- elseif sizeDifference < -0.001 then
- -- Smaller black hole is absorbed by the larger one
- task.wait(0.1)
- resetForces(otherBlackHole)
- applyNewForce(otherBlackHole)
- hole:Destroy()
- else
- -- Both black holes merge into a new one
- local unifiedSize = hole.Size * 2
- local newBlackHole = hole:Clone()
- newBlackHole.Size = unifiedSize
- newBlackHole.Position = hole.Position
- newBlackHole.Parent = hole.Parent
- CollectionService:AddTag(newBlackHole, "Blackholev5")
- -- Update Glint size to be twice as large
- local newGlintSize = glint.Size.Keypoints[1].Value * 2
- newBlackHole.VFX.Glint.Size = NumberSequence.new(newGlintSize)
- hole:Destroy()
- otherBlackHole:Destroy()
- resetForces(newBlackHole)
- applyNewForce(newBlackHole)
- hole = newBlackHole
- end
- end
- -- DAMAGE/KILLER (Kills players when they touch it) --
- function onTouch(part)
- local humanoid = part.Parent:FindFirstChild("Humanoid")
- if humanoid then
- humanoid.Health = 0
- end
- end
- if killPlayers then
- hole.Touched:Connect(onTouch)
- end
- -- Function to scale Glint's Size over time based on the black hole's size
- local function updateGlintSize()
- local currentSize = hole.Size.X
- if currentSize ~= previousSize then
- -- Calculate the scale factor based on size change
- local scaleFactor = currentSize / previousSize
- -- Update the size of Glint
- local size = glint.Size
- local newSize = size.Keypoints[1].Value * scaleFactor
- -- Set the new size to the particle emitter
- glint.Size = NumberSequence.new(newSize)
- -- Update the previous size to the current size for future scaling
- previousSize = currentSize
- end
- end
- -- DEMATERIALIZER (Deletes ANY object that touches it except for other black holes) --
- function onTouched(part)
- if CollectionService:HasTag(part, "Blackholev5") and mergeBlackholes then
- AbsorbSmallerBlackHole(part) -- Absorb the smaller black hole if applicable
- return
- end
- if part.Anchored then
- return -- Skip anchored objects
- end
- if CollectionService:HasTag(part, "Blackholev5") and paradoxHandler then
- return
- end
- -- Calculate average size
- local function CalculateAverageSize(part)
- local size = part.Size
- local averageSize = (size.X + size.Y + size.Z) / 3
- return averageSize
- end
- local objectAverageSize = CalculateAverageSize(part)
- local blackHoleSize = hole.Size.X
- -- Calculate the new size increment based on the object's average size
- local sizeIncrement = (objectAverageSize / (blackHoleSize * sizeProportion))
- part.BrickColor = partColor
- part.Material = partMaterialTransform
- part.CanCollide = collision
- part.Anchored = fixedPosition
- for _, transparency in ipairs(transparencySteps) do
- part.Transparency = transparency
- wait(dematerializeDuration)
- end
- part:Destroy() -- Delete part after completing transparencySteps
- if growth then
- -- Adjust the black hole's size based on the new size increment
- hole.Size = hole.Size + Vector3.new(sizeIncrement, sizeIncrement, sizeIncrement)
- rangeFactor = rangeFactor + sizeIncrement * rangeProportion
- end
- end
- -- Update the Glint size when the size of the hole changes
- game:GetService("RunService").Heartbeat:Connect(updateGlintSize)
- if absorption then
- local connection = hole.Touched:Connect(onTouched)
- print("{BHv5A} Absorption is ENABLED")
- logger.consuming = true
- else
- print("{BHv5A} Absorption is DISABLED")
- logger.consuming = true
- end
- if toggleMicroGravity and workspace.Gravity ~= 0 then
- game.Workspace.Gravity = 0
- print("{BHv5A} Gravity is DISABLED")
- logger.zeroGrav = true
- else
- print("{BHv5A} Gravity is ENABLED")
- logger.zeroGrav = true
- end
- --------
- --[[
- -- [Blackhole v5 Alpha Release Documentation/Manual] Jan. 22, 2025 --
- The most complicated blackhole model in Roblox. a lot of controls that does something with each other, but uses simple maths.
- If you have complaints with the blackhole, please leave a comment on the model page. and please, dont go nerdy on me, I barely know general and special relativities.
- ----------
- I PHYSICAL
- IMPORTANT: Note that the blackhole is a MeshPart, resize the blackhole by holding `alt` to preserve proportions!
- If you want to use a regular sphere, copy and paste every child of the blackhole onto the new sphere.
- Can also be a cube, or custom MeshParts, but gravitational attraction is still spherical.
- `toggleMicroGravity` (quickly disable gravity) can be turned off or on if you dont want to go into the game settings manually.
- Change blackhole properties like `EventHorizon` (pitch black effect), color, transparency, mass, custom physical properties and more to your liking.
- ----------
- ----------
- II GRAVITY
- Blackhole v5 Prototype runs on BodyForce-based gravity. Blackhole v4.0+ runs on Velocity-based gravity, v4 Betas are hybrid-based (force or velocity).
- `gravitationalConstant` is the base strength of the blackhole's overall gravity, which are manipulated by `damping` (suppresses violent simulation between 0 & 1),
- and `rangeFactor` (area in studs where objects are pulled in, anything outside that is not affected. gravity is amplified for bigger values).
- When switching between BodyForce and Velocity gravity, note that gravitational constant is fixed, but the two behave differently. velocity-based is violent!
- BodyForce gravity: Stable, less accurate, customizable range. overall a mode to play around with!
- Velocity gravity: Violent, accurate, unlimited range. overall a mode to simulate blackholes with! (tweaked to synchronize with BodyForce)
- Since there's so many knobs to turn, you can break the blackhole. break it, I dare you. I double dare you. just kidding, just dont break the laws of math!
- ----------
- ----------------------------
- III ABSORPTION & PERFORMANCE
- Objects are absorbed if `absorption` is enabled, which will follow `transparencySteps` (fading effect between 1 & 0 invisibilities)
- in a span of `dematerializeDuration` (time to progress each transparency step) while `partMaterialTransform` (material absorbed objects turn into) and
- `partColor` (color absorbed objects turn into) lead the object into deletion, causing `growth` to make the blackhole bigger and stronger with
- `sizeProportion` (how fast the blackhole grows, bigger means slower growth) and `rangeProportion` (how fast rangeFactor grows, bigger means faster growth).
- `sizeProportion` CANNOT be 0 as it divides, only non-zeroes. Positive = grow, negative = shrink, asymptote to 0 = max size of 2048 studs.
- Sizing: maximum is 2048 xyz, minimum is 0.001. not recommended to have micro blackholes as it causes issues, as well as infinity for range-related variables.
- Gravity: negative `damping` == repel objects like negative `gravitationalConstant`. also not recommended setting values to large numbers.
- `killPlayers` (kills absorbed players) can be turned on or off when simulating with a player. Please disable when doing rapid blackhole merging!
- An error will say {attempt to index nil with 'FindFirstChild'} because it cannot identify any humanoids fast enough.
- `fixedPosition` (anchor absorbed objects) can help with performance, along with `collision` (make objects pass through eachother while being absorbed), and
- `updateInterval` (value in seconds to update gravitational forces. lower values are accurate but slower, higher values are inaccurate but faster).
- If `absorption` is disabled, everything above within ABSORPTION is excluded EXCEPT `killPlayers`.
- ----------------------------
- ------------------
- IV BETA PARAMETERS
- `infinityHandler` prevents objects that crosses `EventHorizon` (AKA the blackhole's volume) from accelerating into infinity at singularity (the very center)
- `paradoxHandler` prevents two blackholes from absorbing eachother when they touch.
- `mergeBlackholes` merges blackholes: bigger blackhole absorbs the smaller, equal blackholes merge into a blackhole twice as big.
- When two different sized blackholes touch eachother with this enabled, it will cause the winning blackhole to accelerate to infinity. [BUG FIXED]
- `gravityEngine` modes between BodyForce-based gravity and Velocity-based gravity. accepts "1" or "force" too, and accepts "2" or "speed" as well, respectively.
- BodyForce-based gravity ensures stable simulations, but a lower accuracy than to Velocity-based gravity, which is more violent, laggy and accurate.
- `glint` is the reference to a cosmetic the blackhole has. when changing its name and or location, you must also change the path of the variable.
- `glintProportion` not to be confused with `glintProportions`, modified value of `glintProportions` multiplied by `glintProportion` to change `glint` proportions.
- `rocheLimitFactor` is the area around the blackhole (multiple of the radius) where any joint is destroyed for realistic simulation. this feature is from v4.0+
- This can be disabled by setting it to zero!
- `gravitySync` synchronizes the gravitational prowess of velocity-based gravity with the force-based gravity without modifying `gravitationalConstant`.
- When disabled, note that velocity-based gravity is significantly more powerfull than force-based gravity, and modification to the constant is needed.
- ------------------
- ----------
- V ADVANCED
- If you know how to code, you can edit the script to your liking.
- Tips:
- In `CheckAndApplyGravity(obj)`, you can change `if not obj:IsDescendantOf(game.Workspace) or not obj:IsA("BasePart") then return end` to exclude specific objects.
- Changing `if obj == hole or obj.Parent:IsA("Player") then return end` will cause glitches. however, you may change `obj.Parent:IsA("Player")` without issues.
- Tinkering with `ApplyGravity(object)` and `onTouched(part)` can throw simulation accuracy off, modify cautiously.
- [NEW!]
- Overriding `infinityHander` code can make the blackhole have strange, but cool behavior. Be careful with your math!
- Examples of overriding the function:
- local holeRadius = hole.Size.Magnitude * 2 -- Increase the area bigger than the blackhole (twice in size). can be any value.
- ...
- if distance < holeRadius then
- force = Vector3.zero -- Removes gravity, acts like a vacuum (make into -force for something epic to happen) [DEFAULT]
- OR
- force = -force -- Inverts gravity, creating a forcefield shell around the blackhole [NEW!]
- direction = Vector3.zero -- Prevents direction inversion for velocity-based logic [KEEP]
- end
- `gravitySync` is not an exact synchronization, only an approximation. If you have a solution to make it exact, modify the following dunction in the main script:
- if engineType == "velocity" or engineType == "speed" or engineType == "2" and gravitySync then
- magnitude = magnitude / (gravitationalConstant * 0.1) -- Dampening factor for velocity-based gravity
- end
- ----------
- -- [Blackhole v5 Alpha Release Update Log] 22/01/2025 --
- [ Alpha v0.4 ]
- MASSIVE UPDATES:
- + Synchronized gravity for both BodyForce and Velocity-based gravities (both still work differently in handling objects)
- + Detailed update log
- SMALL UPDATES:
- + Updated Documentation
- + Error handling for invalid `gravityEngine` requests
- + Overridable `infinityHandler` for epic stuff to happen, more info in Section V of Documentation:
- `infinityHander` code can make the blackhole have strange, but cool behavior. Be careful with your math!
- Examples of overriding the function:
- local holeRadius = hole.Size.Magnitude * 2 -- Increase the area bigger than the blackhole (twice in size). can be any value.
- ...
- if distance < holeRadius then
- force = Vector3.zero -- Removes gravity, acts like a vacuum (make into -force for something epic to happen) [DEFAULT]
- OR
- force = -force -- Inverts gravity, creating a forcefield shell around the blackhole [NEW!]
- direction = Vector3.zero -- Prevents direction inversion for velocity-based logic [KEEP]
- end
- + Added `gravitySync` to equalize the strength of force and velocity-based gravities. more in `Documentation`.
- INCOMING UPDATES:
- = Model support for Velocity-based gravity
- ]]
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement