How to Create a Black Hole

From Goodblox Wiki
Revision as of 17:15, 22 June 2020 by Pizzaboxer (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigationJump to search
This is an Advanced, Lua Script related tutorial.

Introduction

A black hole destroys SonOfSevenLess's castle

This tutorial is designed for advanced scripters and scripters who can be described as medium-level. It will cover several concepts, such as area-of-effect, and will provide the finished code for a black hole at the end.

If you are unable to understand this tutorial, it is suggested that you learn from the Novice tutorials first. If you are merely here to get a black hole, you can find in Roblox Studio in Insert / Free Models / typing "Black Hole" / Clicking "Search" / double-clicking the black hole you want.

Starting off

Before creating a black hole, it is prudent to clear an area and anchor anything in the area which you would like to keep. Beware, as black holes actually increase in power as they gain mass, so the area should probably be at least 128 units in radius. It is also a good idea to figure out EXACTLY what you want the black hole to do. In this case, I have laid out the desired effects and the concepts that I use.

Desired Effects

The black hole will do the following:

  • Suck up ANYTHING that can move within a reasonable radius.
  • Destroy objects that get close enough.

Concepts

  • Anchored The black hole, to be efficient, should not attempt to suck up any brick that cannot move anyway. This also prevents the floor from being destroyed.
  • Area of Effect This black hole will suck ANYTHING that comes within a reasonable distance into itself.
  • Arrays Arrays are sets of variables.
  • Children In order to find every brick, functions will need to find them first.
  • Classes The black hole must only work on certain types of objects, namely "Parts".
  • Iteration Functions will use loops to cycle through arrays.
  • Mass Similar to weight, but without taking gravity into account.
  • Recursion The function for finding objects will call itself.

Variable Initialization

The black hole will need to keep track of a number of things. It will need an array to keep track of objects and a floating-point variable to keep track of its mass (power).

local hole = script.Parent -- Optional. Added for optimization
local childList = {}
local massConstant = 5.8 -- Generally a good value (again, optional)
local mass = 32000 * massConstant

Area of Effect

Area of effect is one of the most important concepts when creating a black hole, or any script intended to affect a wide range of bricks. The basic idea is to find every brick in the map, add them to an array, and use the array later. A recursive function will work well, allowing both simplicity and elegance.

Create a function, in this case called checkObject, which accepts an object's handle as an input. I named this variable obj. This function will first get the array of children using

local list = obj:GetChildren()

If the child's className variable indicates that it is a Part (brick), then it is added to the array of bricks using

table.insert(childList, 1, list[n])

If it is a Model, Workspace, Tool, or Hat, then it is checked for bricks by calling

local list = obj:GetChildren()

(This sets list to an array containing every child) and using a for loop to call

checkObject(list[n])

on every child, and it is made to check for children that are added later by using

list[n].ChildAdded:connect(checkObject)

This works because the ChildAdded event calls function with an argument of the child that was added. This ends the checkObject function. To get all bricks included, the script calls

checkObject(workspace)

Put together, the above code makes this:

-- Note: This should only be run once for each object
function checkObject(obj)
	if (obj ~= hole) and (obj.className == "Part") then
		if (obj.Anchored == false) then
			table.insert(childList, 1, obj)
		end
	elseif (obj.className == "Model") or (obj.className == "Hat") or (obj.className == "Tool") or (obj == workspace) then
		local child = obj:GetChildren()
		for x = 1, #child do
			checkObject(child[x])
		end
		obj.ChildAdded:connect(checkObject)
	end
end

Finished Code

When complete, the code will look something like this:

local hole = script.Parent
local childList = {}

local massConstant = 5.8 -- Generally a good value

local mass = 32000 * massConstant

-- This is basically a function that finds all unanchored parts and adds them to childList.
-- Note: This should only be run once for each object
function checkObject(obj)
	if (obj ~= hole) and (obj.className == "Part") then
		if (obj.Anchored == false) then
			table.insert(childList, 1, obj)
		end
	elseif (obj.className == "Model") or (obj.className == "Hat") or (obj.className == "Tool") or (obj == workspace) then
		local child = obj:GetChildren()
		for x = 1, #child do
			checkObject(child[x])
		end
		obj.ChildAdded:connect(checkObject)
	end
end

checkObject(workspace)

print("Black Hole script loaded.")

local n = 0
while true do
	if n < #childList then
		n = n + 1
		if n % 800 == 0 then
			wait()
		end
	else
		n = 1
		wait()
	end

	local child = childList[n]
	if (child ~= hole) and (child.className == "Part") and (child.Anchored == false) then
		local relPos = hole.Position - child.Position
		local motivator = child:FindFirstChild("BlackHole Influence")
		if relPos.magnitude * 240 * massConstant < mass then
			child:BreakJoints()
			if (relPos.magnitude * 320 * massConstant < mass) and (child.Size.z + hole.Size.x >  relPos.magnitude * 2 - 4) then
				mass = mass + child:GetMass()
				child:Remove()
				table.remove(childList, n)
				n = n - 1 -- This is the reason I need a counter of my own design
			else
				child.CanCollide = false -- I Can assume that things won't escape the black hole.
				if motivator == nil then
					motivator = Instance.new("BodyPosition")
					motivator.Parent = child
					motivator.Name = "BlackHole Influence"
				end
				motivator.position = hole.Position
				motivator.maxForce = Vector3.new(1, 1, 1) * mass * child:GetMass() / (relPos.magnitude * massConstant)
			end
		elseif motivator ~= nil then
			motivator:Remove()
		end
	end
end