It was originally written on Github (by myself). Still remember pls that this is still W.I.P and that new elements still are coming!
Table of Contents
-
Get the Corners of a normal Part
- Sketch/Script
- Edit 1 We make our script shorter and more readable
- Edit 2 We use tables and the tipps of the replys
-
- Sketch/Script
Get the Corners of a normal Part
Content
Sketch/Script
In this tutorial I will deal with getting the corners of a part. These can be used for many other things, for example to control a part into a certain region without using the function game.Workspace:FindPartInRegion3(). Let’s start now. In this tutorial each corner is represented by a part which is 1x1x1. So you can see if the corner is really there where it should be. To get the corners of a non-rotating part you need a formula. For this it is better to paint a sketch. Here is what I painted (I’m not an artist so please no criticism):
In this sketch you can see how you can get all the corners of a normal part. The technique is to use vectors, just being able to read a bit, turn everything into vectors and there you have it! Because I can’t paint at all I will explain it manually: say we want to get point A, then we have to use everything we know. The point in the middle is the position and we know that our target position is in the upper left corner at the back, and this one is also at the back. With all this we can calculate the position of A: left is -x, top is y and back is -z, we calculate everything together and get Vector3.new(-x, y,-z). But wait a minute, @Eternalove_fan32 didn’t you say that we need the vectors x, y and z, where do we get them? If it was a cube then we could just use one size. But the case is not always the same and sometimes the size could be 3x1x5, not necessarily a cube. Therefore we should use any size. So our three vectors (I mean the -x, y, -z) are not different from the corresponding size, so you could also write the position of point A as Vector.new(-Size.X, Size.Y, -Size.Z) Oh, I forgot to say: the size and position should be the part where you want to get the corners from.
The result is this:
local Parent = script.Parent
local Position = Parent.CFrame
local Size = Parent.Size
local PartA = Instance.new("Part", workspace)
local PartB = Instance.new("Part", workspace)
local PartC = Instance.new("Part", workspace)
local PartD = Instance.new("Part", workspace)
local PartE = Instance.new("Part", workspace)
local PartF = Instance.new("Part", workspace)
local PartG = Instance.new("Part", workspace)
local PartH = Instance.new("Part", workspace)
PartA.Name = "A"
PartB.Name = "B"
PartC.Name = "C"
PartD.Name = "D"
PartE.Name = "E"
PartF.Name = "F"
PartG.Name = "G"
PartH.Name = "H"
PartA.CanCollide = false
PartB.CanCollide = false
PartC.CanCollide = false
PartD.CanCollide = false
PartE.CanCollide = false
PartF.CanCollide = false
PartG.CanCollide = false
PartH.CanCollide = false
PartA.Anchored = true
PartB.Anchored = true
PartC.Anchored = true
PartD.Anchored = true
PartE.Anchored = true
PartF.Anchored = true
PartG.Anchored = true
PartH.Anchored = true
PartA.Size = Vector3.new(1,1,1)
PartB.Size = Vector3.new(1,1,1)
PartC.Size = Vector3.new(1,1,1)
PartD.Size = Vector3.new(1,1,1)
PartE.Size = Vector3.new(1,1,1)
PartF.Size = Vector3.new(1,1,1)
PartG.Size = Vector3.new(1,1,1)
PartH.Size = Vector3.new(1,1,1)
PartA.CFrame = CFrame.new(Position.Position + Vector3.new(-Size.X/2, Size.Y/2, -Size.Z/2))
PartB.CFrame = CFrame.new(Position.Position + Vector3.new(-Size.X/2, Size.Y/2, Size.Z/2))
PartC.CFrame = CFrame.new(Position.Position - Size/2)
PartD.CFrame = CFrame.new(Position.Position + Vector3.new(Size.X/2 , -Size.Y/2 , Size.Z/2))
PartE.CFrame = CFrame.new(Position.Position + Vector3.new(Size.X/2 , -Size.Y/2 , -Size.Z/2))
PartF.CFrame = CFrame.new(Position.Position + Vector3.new(Size.X/2 , Size.Y/2 , -Size.Z/2))
PartG.CFrame = CFrame.new(Position.Position + Size/2)
PartH.CFrame = CFrame.new(Position.Position + Vector3.new(-Size.X/2 , -Size.Y/2 , Size.Z/2))
Yes, it works, but it still remains a problem:
As you can see, if our part turns, our corners don’t position themselves where they should and this can lead to unwanted results. This part was the hardest, but could be solved with the help of Elcore (I asked here for help). To solve this problem you need to use CFrames to transform the CFrame of the corners of the part in the object space, as it says in the wiki we just have to multiply the two CFrames and the final result:
Edit 1 We make our script shorter and more readable
This script can of course be written shorter:
--The two scripts must be in the part from which you want to get the corners.
--First Script
local Position = script.Parent.CFrame
local Size = script.Parent.Size
local RunService = game:GetService("RunService")
local Event = script.Parent.Event
local Corners = {
local PartA = Instance.new("Part", workspace)
local PartB = Instance.new("Part", workspace)
local PartC = Instance.new("Part", workspace)
local PartD = Instance.new("Part", workspace)
local PartE = Instance.new("Part", workspace)
local PartF = Instance.new("Part", workspace)
local PartG = Instance.new("Part", workspace)
local PartH = Instance.new("Part", workspace)
}
Corners.PartA.Name = "A"
Corners.PartB.Name = "B"
Corners.PartC.Name = "C"
Corners.PartD.Name = "D"
Corners.PartE.Name = "E"
Corners.PartF.Name = "F"
Corners.PartG.Name = "G"
Corners.PartH.Name = "H"
for i,v in pairs(Corners) do
v.CanCollide = false
v.Anchored = true
v.Size = Vector3.new(1,1,1)
end
RunService.Heartbeat:Connect(function()
local Parent = script.Parent
local Position = Parent.CFrame
local Size = Parent.Size
Event:Fire(Corners.PartA, Corners.PartB, Corners.PartC, Corners.PartD, Corners.PartE, Corners.PartF, Corners.PartG, Corners.PartH, Position, Size)
end)
--Second Script
local Event = script.Parent.Event
Event.Event:Connect(function(PartA,PartB,PartC,PartD,PartE,PartF,PartG,PartH,Position,Size)
PartA.CFrame = Position*CFrame.new(-Size.X/2, Size.Y/2, -Size.Z/2)
PartB.CFrame = Position*CFrame.new(-Size.X/2, Size.Y/2, Size.Z/2)
PartC.CFrame = Position*CFrame.new(-Size/2)
PartD.CFrame = Position*CFrame.new(Size.X/2 , -Size.Y/2 , Size.Z/2)
PartE.CFrame = Position*CFrame.new(Size.X/2 , -Size.Y/2 , -Size.Z/2)
PartF.CFrame = Position*CFrame.new(Size.X/2 , Size.Y/2 , -Size.Z/2)
PartG.CFrame = Position*CFrame.new(Size/2)
PartH.CFrame = Position*CFrame.new(-Size.X/2 , -Size.Y/2 , Size.Z/2)
end)
So we have a shorter script and it is more readable.
Edit 2 We use tables and the tipps of the replys
As recommended by @starmaq and @Cinema_Sin , I will optimize the script thanks to loops and other little things:
--This is only one script, not two.
--Variables
local Position = script.Parent.CFrame
local Size = script.Parent.Size
local RunService = game:GetService("RunService")
--This is a table with all the corners inside
local Vertex = {
local PartA = Instance.new("Part")
local PartB = Instance.new("Part")
local PartC = Instance.new("Part")
local PartD = Instance.new("Part")
local PartE = Instance.new("Part")
local PartF = Instance.new("Part")
local PartG = Instance.new("Part")
local PartH = Instance.new("Part")
}
--Give a name to all corner
Vertex.PartA.Name = "A"
Vertex.PartB.Name = "B"
Vertex.PartC.Name = "C"
Vertex.PartD.Name = "D"
Vertex.PartE.Name = "E"
Vertex.PartF.Name = "F"
Vertex.PartG.Name = "G"
Vertex.PartH.Name = "H"
for _,vertex in pairs(Vertex) do --Go trough the table and set this properties
vertex.CanCollide = false
vertex.Anchored = true
vertex.Size = Vector3.new(1,1,1)
end
RunService.Heartbeat:Connect(function()
--Update us variables
Position = script.Parent.CFrame
Size = script.Parent.Size
--Then change the Position of the Corner (here represented by a Part)
Vertex.PartA.CFrame = Position*CFrame.new(-Size.X/2, Size.Y/2, -Size.Z/2)
Vertex.PartB.CFrame = Position*CFrame.new(-Size.X/2, Size.Y/2, Size.Z/2)
Vertex.PartC.CFrame = Position*CFrame.new(-Size/2)
Vertex.PartD.CFrame = Position*CFrame.new(Size.X/2 , -Size.Y/2 , Size.Z/2)
Vertex.PartE.CFrame = Position*CFrame.new(Size.X/2 , -Size.Y/2 , -Size.Z/2)
Vertex.PartF.CFrame = Position*CFrame.new(Size.X/2 , Size.Y/2 , -Size.Z/2)
Vertex.PartG.CFrame = Position*CFrame.new(Size/2)
Vertex.PartH.CFrame = Position*CFrame.new(-Size.X/2 , -Size.Y/2 , Size.Z/2)
end)
```Lua
--The two scripts must be in the part from which you want to get the corners.
--First Script
local Position = script.Parent.CFrame
local Size = script.Parent.Size
local RunService = game:GetService("RunService")
local Event = script.Parent.Event
local PartA = Instance.new("Part", workspace)
local PartB = Instance.new("Part", workspace)
local PartC = Instance.new("Part", workspace)
local PartD = Instance.new("Part", workspace)
local PartE = Instance.new("Part", workspace)
local PartF = Instance.new("Part", workspace)
local PartG = Instance.new("Part", workspace)
local PartH = Instance.new("Part", workspace)
PartA.Name = "A"
PartB.Name = "B"
PartC.Name = "C"
PartD.Name = "D"
PartE.Name = "E"
PartF.Name = "F"
PartG.Name = "G"
PartH.Name = "H"
PartA.CanCollide = false
PartB.CanCollide = false
PartC.CanCollide = false
PartD.CanCollide = false
PartE.CanCollide = false
PartF.CanCollide = false
PartG.CanCollide = false
PartH.CanCollide = false
PartA.Anchored = true
PartB.Anchored = true
PartC.Anchored = true
PartD.Anchored = true
PartE.Anchored = true
PartF.Anchored = true
PartG.Anchored = true
PartH.Anchored = true
PartA.Size = Vector3.new(1,1,1)
PartB.Size = Vector3.new(1,1,1)
PartC.Size = Vector3.new(1,1,1)
PartD.Size = Vector3.new(1,1,1)
PartE.Size = Vector3.new(1,1,1)
PartF.Size = Vector3.new(1,1,1)
PartG.Size = Vector3.new(1,1,1)
PartH.Size = Vector3.new(1,1,1)
RunService.Heartbeat:Connect(function()
Event:Fire(PartA,PartB,PartC,PartD,PartE,PartF,PartG,PartH,script.Parent.CFrame,script.Parent.Size)
end)
--Second Script
local Event = script.Parent.Event
Event.Event:Connect(function(PartA,PartB,PartC,PartD,PartE,PartF,PartG,PartH,Position,Size)
PartA.CFrame = Position*CFrame.new(-Size.X/2, Size.Y/2, -Size.Z/2)
PartB.CFrame = Position*CFrame.new(-Size.X/2, Size.Y/2, Size.Z/2)
PartC.CFrame = Position*CFrame.new(-Size/2)
PartD.CFrame = Position*CFrame.new(Size.X/2 , -Size.Y/2 , Size.Z/2)
PartE.CFrame = Position*CFrame.new(Size.X/2 , -Size.Y/2 , -Size.Z/2)
PartF.CFrame = Position*CFrame.new(Size.X/2 , Size.Y/2 , -Size.Z/2)
PartG.CFrame = Position*CFrame.new(Size/2)
PartH.CFrame = Position*CFrame.new(-Size.X/2 , -Size.Y/2 , Size.Z/2)
end)
Get Corners of a Sphere
Content
Sketch/Script
In this tutorial I will deal with getting the corners of a sphere shaped part. These can be used for many other things, for example to control a part into a certain region without using the function game.Workspace:FindPartInRegion3(). Let’s start now. In this tutorial each corner is represented by a part which is 0.01x0.01x0.01. So you can see if the corner is really there where it should be. To get the corners of a non-rotating part you need a formula. For this it is better to paint a sketch. Here is what I painted (I’m not an artist so please no criticism):
In this sketch you can see how you can get all the corners of a sphere. I will use vectors, then only see the Picture and with a bit math we have our Idea!
Okay, but I can’t read it. What are all these arrows? And what is this, or this and this? It’s okay, though, because I’m here to help you. Like I said, I used vectors and because it’s hard to paint in 3D, I painted it in 2D. Before we start to calculate the vertex of the ball we have to know the normal position. For this there is nothing better than a ball with radius 1 and the position at 0,0,0, so the center of the room, then export the whole thing as OBJ file and finally in a program, like Blender. For those of you who wonder why: How else can we get the vertices? Roblox does not currently allow to get the vertices in itself, so we need another program. In Blender, we should cut the ball in four through the vertical, then cut the ball in one through the horizontal. In total we should have 8 pieces. From here on everybody has his own method, but I would personally take the 1/8th that is positive, for those who don’t figure out what I mean here is a picture of it:
Then we can switch to edit mode in Blender so you can move, modify, add vertices and most importantly: get their position. In Blender we then switch to this edit mode by pressing the Tab key or finding the GUI of it and just clicking it. Then we should press the N key: to the right of the viewport frame there should be a new GUI. Now comes one of the most boring and exhausting work there is: Getting every single vertex of that 1/8th of an inch. To get a better overview we should delete the other 7/8, be careful to delete only these vertices, not the whole OBJ model.
When in Blender, move the camera with the middle button of the mouse. Attention: You need a mouse and no laptop integrated mouse. Then there are many other keys, but because Blender has so many key combinations I can’t tell you all the basics. With the Z key you can see through the model by selecting wireframe. Then with the B key you can select multiple vertices and with the X key you can delete the selected vertices by selecting vertices.
If you select a vertex with the left mouse click then the position of the vertex should appear in the GUI that came with the N key: By the way, you have to be careful because Blender uses the real vector system, so while in Roblox you form a vector like this: Vector3.new(x,y,z), the real vector system looks like this: Vector3.new(x,z,y), the z and y are fooled, so for example the height is the Z axis and not the Y axis. Then my plan is to add all received positions to the table. Now the question comes up: Why then use only 1/8 of the whole ball and not get all positions from every single vertex? The reason is simple: A RobloxBall has over 100 vertices, so it would be a real pain to control, copy and vectorize everything one by one. Why this particular 1/8th (Small Edit: its the same as a upperquadrant, sorry if i just noticed it yet)? You will see later.
Now, we go into studio then we have to make a table (it’s recommended by me, otherwise it looks like a catastrophe):
local Vertices = {
Vector3.new(0.5, -0.000107, -0.000009),
Vector3.new(0.474, -0.000107, 0.15811),
Vector3.new(0.416, -0.000107, 0.27734),
Vector3.new(0.354, -0.000107, 0.35355),
Vector3.new(0.474, 0.15799, -0.000009),
Vector3.new(0.452, 0.15069, 0.15075),
Vector3.new(0.40099, 0.13349, 0.26726),
Vector3.new(0.34399, 0.11459, 0.34412),
Vector3.new(0.416, 0.27729, -0.000009),
Vector3.new(0.40099, 0.26719, 0.13362),
Vector3.new(0.364, 0.24239, 0.24253),
Vector3.new(0.32, 0.21309, 0.3198),
Vector3.new(0.354, 0.35349, -0.000009),
Vector3.new(0.34399, 0.34399, 0.1147),
Vector3.new(0.32, 0.31969, 0.21319),
Vector3.new(0.28899, 0.28859, 0.28867),
Vector3.new(0.277, 0.41589, -0.000009),
Vector3.new(0.267, 0.40079, 0.13362),
Vector3.new(0.243, 0.36369, 0.24253),
Vector3.new(0.158, 0.47419, -0.000009),
Vector3.new(0.15099, 0.45219, 0.15075),
Vector3.new(0, 0.49989, -0.000009),
Vector3.new(0.277, -0.000107, 0.41602),
Vector3.new(0.158, -0.000107, 0.47434),
Vector3.new(0, -0.000107, 0.49999),
Vector3.new(0.267, 0.40089, 0.000009),
Vector3.new(0.15099, 0.15069, 0.45226),
Vector3.new(0, 0.15799, 0.47434),
Vector3.new(0.243, 0.24239, 0.3638),
Vector3.new(0.134, 0.267219, 0.40089),
Vector3.new(0, 0.27729, 0.41602),
Vector3.new(0.213, 0.31969, 0.3198),
Vector3.new(0.115, 0.34399, 0.34412),
Vector3.new(0, 0.35349, 0.35355),
Vector3.new(0.134, 0.40079, 0.26726),
Vector3.new(0, 0.41589, 0.27734),
Vector3.new(0, 0.47419, 0.15811),
Vector3.new(0.267, 0.40089, 0.13349),
Vector3.new(0.267, 0.13349, 0.40089)
}
So now we have reached every vertex, but it does not help us in this state. That’s where the skis come in: We multiply the position (by which I mean the CFrame) of the ball by the position of each vertex in the table. Now you’ll probably say: “But don’t tell me that you’re really going to multiply each vertex by the position (I still mean the CFrame) of the ball: @Eternalove_fan32, you have really gone crazy now.” After this tutorial I might really have become . Jokes on page, math on. No, I’m so stupid of course, I’ll just use loops for that, which will result:
--The Vertice Table over this line and insert this script into the target sphere
--The Folder in them we are going stock all the Corners Parts since we use Parts for seeing the invisible corners
local VertexFolder = Instance.new("Folder")
VertexFolder.Parent = workspace
VertexFolder.Name = "SphereVertex"
--The "Position" of the sphere, and with position i mean the CFrame
local Position = script.Parent.CFrame
--The loop
for index, vert in pairs(Vertices) do
--For each Vertex make a Part with Size 0.01^3, Red Color and as Parent the Folder
local Vertex = Instance.new("Part")
Vertex.Parent = VertexFolder
Vertex.BrickColor = BrickColor.Red()
Vertex.Size = Vector3.new(0.01, 0.01, 0.01)
Vertex.CanCollide = false
Vertex.Anchored = true
Vertex.Name = "Vertex"..index --The Name: only concat the string Vertex with the index
Vertex.Position = Position * vert --Get us target Position
end
Okay, but Eterna, you said we should take 1/8 of it, and it had to be 1/8 of the positive, but why? And why not other 1/8ths? Ok, you can also work with one of the other 1/8, but the corner (I don’t want to repeat the 1/8 all the time) that I recommended is the easy one: If we start the script now, you can see that:
Fine, but where are the other vertices? Simple: They are not there because they are not in the table. So, who wants to define more than 200 vertices in this table? Certainly not me. That’s why the loops exist: It can do all the hard work for us:
--The Vertices Table over this line
for _,vert in pairs(Vertices) do
local Mirrored = -vert
local MirroredX = Vector3.new(-vert.X, vert.Y, vert.Z)
local MirroredY = Vector3.new(vert.X, -vert.Y, vert.Z)
local MirroredZ = Vector3.new(vert.X, vert.Y, -vert.Z)
if table.find(Vertices, Mirrored) == "nil" then --If it not find the already mirrored vertex, then insert it in the table
table.insert(Vertices, #Vertices + 1, Mirrored)
elseif table.find(Vertices, MirroredX) == "nil" then
table.insert(Vertices, #Vertices + 1, MirroredX)
elseif table.find(Vertices, MirroredY) == "nil" then
table.insert(Vertices, #Vertices + 1, MirroredY)
elseif table.find(Vertices, MirroredZ) == "nil" then
table.insert(Vertices, #Vertices + 1, MirroredZ)
end
end
Now we have two problems, the first one comes when you try another size, while the second comes when you try the script from up here, the script takes more than 10 seconds before it shows us the vertices. Ten seconds is quite long and can change everything, especially in fast games as well as action games, fighting games and so on. There is only one way to speed it up: put everything into the table. I used a loop and print system, but if you want to know in detail, just ask down here. The final table is this one:
https://github.com/DragonGamerDevelopers/Roblox/blob/master/TooLargeTables/VerticesOfaRobloxSphere.txt
Now we can deal with the second problem: if we now take the ball with radius 1 then we get this result:
But if you try another size:
The problem is simple: Since we took the vertices of the ball with radius 1, it is calculated with radius 1, although it is not absolutely necessary now. So, how can we solve it? The solution is very simple, just multiply the vertex with the size and then we have also adjusted the vertex to the size:
And we’re practically finished! You can now use the vertexes of your RobloxBall the way you want. Final script:
Be sure to follow this post to be the first to be warned of possible new methods, new ways to get their vertex from others shapes or just always be warned.
Any questions or suggestions? Put them down here I’ll be happy to answer them.
Need help? Just ask!
I hope you enjoyed it and that it helped you. Final Script:
~~Eterna