How to Create a Black Hole: Difference between revisions

From Goodblox Wiki
Jump to navigationJump to search
(Created page with "{{CatUp|Tutorials}} __TOC__ == Introduction == There are two ways you can make ramps. You can either use a Hinge or a CFrame to edit rotation. <!------------------------------...")
 
No edit summary
 
Line 1: Line 1:
{{CatUp|Tutorials}}
{{CatUp|Tutorials}}
{{ScriptTutorial|Advanced|Lua Script}}
__TOC__
__TOC__
== Introduction ==
There are two ways you can make ramps. You can either use a Hinge or a CFrame to edit rotation.
<!-----------------------------------------------------------------------------------
If you're editing this to post about the Wedge, you'd better not waste your time. A Wedge is NOT a ramp, it's still a square brick. These are ramps that make real ramps, a wedge does not do that. I've already warned that the next one to post about a wedge gets a ban. So don't do it. -------------------------------------------------------------------------------->


==Using a Hinge==
==Introduction==
[[Image:BlackHole 01.jpg|thumb|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|finished code]] for a black hole at the end.


* In [[GoodBlox Studio|GoodBlox studio]], click Workspace.
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.
* Click Insert > Object > Part
* [[Resize]] the newly brick so that it is one unit wide, one unit long, and as tall as you want.
* Click the [[Weld]] option in your [[Toolbar]] (it has an X in a square).
* Click ONE side of your tall brick with the weld option.
* Click Workspace.
* Click Insert > Object > Part
* [[Resize]] this newly created brick so it is one unit wide, one unit long, and one unit tall.
* Click the [[Weld]] option in your [[Toolbar]].
* Click the side of this brick that is facing the welded side of the TALL brick with the weld option. You should have two welded faces facing each other.
* Select the [[Drag]] option in your [[Toolbar]].
* Place the small brick near the top of the tall tower.
* Select the [[Hinge]] option on your [[Toolbar]]
* Click the small brick.  A yellow dot should be protruding from it.
* Click Workspace.
* Click Insert > Object > Part
* [[Resize]] this newly created brick to a long, flat, rectangle.
* [[Drag]] this newly created rectangle so that you can't see the yellow dot.
* Test your game.  If you did this correctly, your bar should fall downwards.


[[Image:Ramp.JPG]]
== Starting off ==


==Using a CFrame==
Before creating a black hole, it is prudent to clear an area and [[Anchored|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. 


Make a brick, anchor it and name it "slope". Then open the [[Scripting#Fundamentals|Command Bar]] and type this in:
===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.


<pre>
==Variable Initialization==
game.Workspace.slope.CFrame=CFrame.fromEulerAnglesXYZ(0,0,0)
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).
</pre>


Change the "(0,0,0)" to how many [http://en.wikipedia.org/wiki/Radian radians] (not degrees) you want it to rotate.  Instead of using [http://en.wikipedia.org/wiki/Degree_%28angle%29 degrees], they use radians, which are a different way of saying how big an angle is.  You can use numbers between 1 and 0, which will work fine.
<pre>local hole = script.Parent -- Optional. Added for optimization
local childList = {}
local massConstant = 5.8 -- Generally a good value (again, optional)
local mass = 32000 * massConstant</pre>


Play around with it, you can put the new number in either one of the three zeroes in the line, like this:
==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.


<pre>
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
game.Workspace.slope.CFrame=CFrame.fromEulerAnglesXYZ(0.5,0.9,0.3)
</pre>


Which will make it rotate in all kinds of directions.  You can also press enter a couple of times and it will make the brick rotate again, so you can rotate it farther.
<pre>local list = obj:GetChildren()</pre>


Also, you can use
If the child's ''className'' variable indicates that it is a ''Part'' (brick), then it is added to the array of bricks using
<pre>
game.Workspace.slope.CFrame=CFrame.new(Vector3.new(0,100,0)) * CFrame.fromAxisAngle(Vector3.new(0,0,1), math.pi/2)
</pre>


* Change the first CFrame.new(Vector3.new((x,y,z)) values to reflect the position of your brick.
<pre>table.insert(childList, 1, list[n])</pre>
* Change the second CFrame.fromAxisAngle(Vector3.new(x,y,z)) values to reflect which dimensions you are rotating.  For example, if you want to rotate the brick around the z axis, use (Vector3.new(0,0,1)).  If you want to rotate the brick around the y axis, use (Vector3.new(0,1,0)).  If you want to rotate the brick around the x and z axis, use (Vector3.new(1,0,1)).
* Lastly, change math.pi/2 to the value in radians you wish to angle the brick by.  This can be a constant, a variable, up to you.


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


[[Radians]]
<pre>local list = obj:GetChildren()</pre>


[[Function Dump/Mathematical Functions]]
(This sets ''list'' to an array containing every child) and using a ''for loop'' to call
 
<pre>checkObject(list[n])</pre>
 
on every child, and it is made to check for children that are added later by using
 
<pre>list[n].ChildAdded:connect(checkObject)</pre>
 
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
 
<pre>checkObject(workspace)</pre>
 
Put together, the above code makes this:
 
<pre>-- 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</pre>
 
==Finished Code==
When complete, the code will look something like this:
 
<pre>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</pre>


[[Category:Building Tutorials]]
[[Category:Scripting Tutorials]]
[[Category:Scripting Tutorials]]

Latest revision as of 17:15, 22 June 2020

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