It’s completely painful to use manmade arbtirary sets as identifiers for surface size. Yes, it “gets the job done”, but I’ve been taking 2 hours to still make the if statements and it’s hurting my brain
P A I N
(If you don’t know what is happening, if I click anywhere other than the line I’m working on it resets my scrollbar)
This only works for cubes.
local Players = game:GetService("Players")
local player = Players.LocalPlayer
local mouse = player:GetMouse()
local ref = {
[Enum.NormalId.Left] = Vector3.new(1, 1, 0),
[Enum.NormalId.Right] = Vector3.new(1, 1, 0),
[Enum.NormalId.Bottom] = Vector3.new(1, 0, 1),
[Enum.NormalId.Top] = Vector3.new(1, 0, 1),
[Enum.NormalId.Back] = Vector3.new(0, 1, 1),
[Enum.NormalId.Front] = Vector3.new(0, 1, 1)
}
while wait(1) do
local target = mouse.Target
if target then
local size = mouse.Target.Size * ref[mouse.TargetSurface]
print(size)
local sX = size.X == 0 and 1 or size.X
local sY = size.Y == 0 and 1 or size.Y
local sZ = size.Z == 0 and 1 or size.Z
local surfaceArea = sX * sY * sZ
print(surfaceArea)
end
end
If you want to do one for every part then that’s impossible, because MeshParts exist. I’m pretty sure that there’s no current possible way to calculate their surface areas.
Okay… I might have needed to post my build script.
local e = false
local qr
local tb
local mou = game.Players.LocalPlayer:GetMouse()
local function raysurfaceface(e,g)
local dotY = g.CFrame:VectorToObjectSpace(e):Dot(Vector3.new(0,1,0))
local dotX = g.CFrame:VectorToObjectSpace(e):Dot(Vector3.new(1,0,0))
local dotZ = g.CFrame:VectorToObjectSpace(e):Dot(Vector3.new(0,0,1))
local surface
if (math.abs(dotY) >= 0.9) then
if (dotY < 0) then
surface = Enum.NormalId.Bottom
else
surface = Enum.NormalId.Top
end
elseif (math.abs(dotX) >= 0.9) then
if (dotX < 0) then
surface = Enum.NormalId.Left
else
surface = Enum.NormalId.Right
end
else
if (dotZ < 0) then
surface = Enum.NormalId.Front
else
surface = Enum.NormalId.Back
end
end
return surface
end
local function vec3Func(f, ...)
local x, y, z = {}, {}, {}
for i, v in next, {...} do
x[i], y[i], z[i] = v.x, v.y, v.z
end
return Vector3.new(f(unpack(x)), f(unpack(y)), f(unpack(z)))
end
local function getRotationBetween(u, v, axis)
local dot, uxv = u:Dot(v), u:Cross(v)
if (dot < -0.99999) then return CFrame.fromAxisAngle(axis, math.pi) end
return CFrame.new(0, 0, 0, uxv.x, uxv.y, uxv.z, 1 + dot)
end
local function getSurfaceCF(part, lnormal)
local pcf, size2 = part.CFrame, part.Size/2
local transition = getRotationBetween(Vector3.new(0,0,1), lnormal, Vector3.new(0,1,0))
local size = vec3Func(math.abs, transition:VectorToWorldSpace(part.Size))
return (pcf * transition) * CFrame.new(size/2 * Vector3.new(0, 0, 1)), size
end
local function boundingbox(size,cf)
local a = {
Vector3.new(0,0,0)+cf.RightVector.Unit*size.X/2+cf.UpVector.Unit*size.Y/2+cf.LookVector.Unit*size.Z/2,
Vector3.new(0,0,0)+cf.RightVector.Unit*size.X/2+cf.UpVector.Unit*size.Y/2-cf.LookVector.Unit*size.Z/2,
Vector3.new(0,0,0)+cf.RightVector.Unit*size.X/2-cf.UpVector.Unit*size.Y/2+cf.LookVector.Unit*size.Z/2,
Vector3.new(0,0,0)+cf.RightVector.Unit*size.X/2-cf.UpVector.Unit*size.Y/2-cf.LookVector.Unit*size.Z/2,
Vector3.new(0,0,0)-cf.RightVector.Unit*size.X/2+cf.UpVector.Unit*size.Y/2+cf.LookVector.Unit*size.Z/2,
Vector3.new(0,0,0)-cf.RightVector.Unit*size.X/2+cf.UpVector.Unit*size.Y/2-cf.LookVector.Unit*size.Z/2,
Vector3.new(0,0,0)-cf.RightVector.Unit*size.X/2-cf.UpVector.Unit*size.Y/2+cf.LookVector.Unit*size.Z/2,
Vector3.new(0,0,0)-cf.RightVector.Unit*size.X/2-cf.UpVector.Unit*size.Y/2-cf.LookVector.Unit*size.Z/2
}
local minlpx = 0
local minlpy = 0
local minlpz = 0
local maxlpx = 0
local maxlpy = 0
local maxlpz = 0
for i=1,#a do
if a[i].X<minlpx then
minlpx=a[i].X
end
end
for i=1,#a do
if a[i].Y<minlpy then
minlpy=a[i].Y
end
end
for i=1,#a do
if a[i].Z<minlpz then
minlpz=a[i].Z
end
end
for i=1,#a do
if a[i].X>maxlpx then
maxlpx=a[i].X
end
end
for i=1,#a do
if a[i].Y>maxlpy then
maxlpy=a[i].Y
end
end
for i=1,#a do
if a[i].Z>maxlpz then
maxlpz=a[i].Z
end
end
return Vector3.new(minlpx,minlpy,minlpz), Vector3.new(maxlpx,maxlpy,maxlpz)
end
local tb = Instance.new("Part",workspace)
tb.Anchored=true
script.Event.Event:Connect(function(q,r)
if e==false then
e = true
qr = q
end
end)
game:GetService("RunService").RenderStepped:Connect(function()
if e==true then
if qr then
local rcp = RaycastParams.new()
rcp.FilterType=Enum.RaycastFilterType.Blacklist
rcp.FilterDescendantsInstances={game.Players.LocalPlayer.Character,tb}
local ray = workspace:Raycast(workspace.CurrentCamera.CFrame.Position,(mou.Hit.p-workspace.CurrentCamera.CFrame.Position).Unit*500,rcp)
if ray==nil then
tb.Parent=nil
else
local f1 = 0
local f2
print(ray.Normal.Unit)
if math.abs(ray.Normal.Unit.X)>f1 then
f1=math.abs(ray.Normal.Unit.X)
f2 = "x"
end
if math.abs(ray.Normal.Unit.Y)>f1 then
f1=math.abs(ray.Normal.Unit.Y)
f2 = "y"
end
if math.abs(ray.Normal.Unit.Z)>f1 then
f1=math.abs(ray.Normal.Unit.Z)
f2 = "z"
end
local r = 45
local jarsx,jarsy,jarsz
if f2=="y" then
jarsx,jarsy,jarsz = ((CFrame.lookAt(Vector3.new(0,0,0),ray.Normal.Unit,ray.Instance.CFrame:VectorToWorldSpace(Vector3.new(0,1,0)))*CFrame.fromOrientation(math.rad(-90),0,0))*CFrame.fromOrientation(0,math.rad(r),0)):ToOrientation()
else
jarsx,jarsy,jarsz = ((CFrame.lookAt(Vector3.new(0,0,0),ray.Normal.Unit,Vector3.new(0,1,0))*CFrame.fromOrientation(math.rad(-90),0,0))*CFrame.fromOrientation(0,math.rad(r),0)):ToOrientation()
end
tb.Parent=workspace
local i1 = Vector3.new(ray.Position.X,ray.Position.Y,ray.Position.Z)
--if raysurfaceface(ray.Instance.CFrame:VectorToObjectSpace(ray.Normal))==Enum.NormalId.Bottom then
-- i1=i1-(tb.PrimaryPart.CFrame.UpVector.Unit*(tb.PrimaryPart.Size.Y/2))
--elseif raysurfaceface(ray.Instance.CFrame:VectorToObjectSpace(ray.Normal))==Enum.NormalId.Top then
-- i1=i1+(tb.PrimaryPart.CFrame.UpVector.Unit*(tb.PrimaryPart.Size.Y/2))
--elseif raysurfaceface(ray.Instance.CFrame:VectorToObjectSpace(ray.Normal))==Enum.NormalId.Front then
-- i1=i1+(tb.PrimaryPart.CFrame.LookVector.Unit*(tb.PrimaryPart.Size.Z/2))
--elseif raysurfaceface(ray.Instance.CFrame:VectorToObjectSpace(ray.Normal))==Enum.NormalId.Back then
-- i1=i1-(tb.PrimaryPart.CFrame.LookVector.Unit*(tb.PrimaryPart.Size.Z/2))
--elseif raysurfaceface(ray.Instance.CFrame:VectorToObjectSpace(ray.Normal))==Enum.NormalId.Right then
-- i1=i1+(tb.PrimaryPart.CFrame.RightVector.Unit*(tb.PrimaryPart.Size.X/2))
--elseif raysurfaceface(ray.Instance.CFrame:VectorToObjectSpace(ray.Normal))==Enum.NormalId.Left then
-- i1=i1-(tb.PrimaryPart.CFrame.RightVector.Unit*(tb.PrimaryPart.Size.X/2))
--end
local intended = CFrame.new(i1)*CFrame.fromOrientation(jarsx,jarsy,jarsz)
local p1,p2 = boundingbox(tb.Size,CFrame.fromOrientation(jarsx,jarsy,jarsz))
local intended2
print(f2)
local kars
if raysurfaceface(ray.Normal.Unit,ray.Instance)==Enum.NormalId.Top or raysurfaceface(ray.Normal.Unit,ray.Instance)==Enum.NormalId.Bottom then
kars = "y"
elseif raysurfaceface(ray.Normal.Unit,ray.Instance)==Enum.NormalId.Front or raysurfaceface(ray.Normal.Unit,ray.Instance)==Enum.NormalId.Back then
kars = "z"
elseif raysurfaceface(ray.Normal.Unit,ray.Instance)==Enum.NormalId.Right or raysurfaceface(ray.Normal.Unit,ray.Instance)==Enum.NormalId.Left then
kars = "x"
end
if f2=="y" then
if kars=="y" then
print('ya')
intended2 = CFrame.new(Vector3.new(math.clamp(ray.Instance.CFrame:PointToObjectSpace(intended.Position).X,-ray.Instance.Size.X/2+math.abs(p1.X-p2.X)/2,ray.Instance.Size.X/2-math.abs(p1.X-p2.X)/2),ray.Instance.CFrame:PointToObjectSpace(intended.Position).Y,math.clamp(ray.Instance.CFrame:PointToObjectSpace(intended.Position).Z,-ray.Instance.Size.Z/2+math.abs(p1.Z-p2.Z)/2,ray.Instance.Size.Z/2-math.abs(p1.Z-p2.Z)/2)))
elseif kars=="z" then
print('za')
print(ray.Instance.CFrame:PointToObjectSpace(intended.Position).Y)
intended2 = CFrame.new(Vector3.new(math.clamp(ray.Instance.CFrame:PointToObjectSpace(intended.Position).X,-ray.Instance.Size.X/2+math.abs(p1.X-p2.X)/2,ray.Instance.Size.X/2-math.abs(p1.X-p2.X)/2),math.clamp(ray.Instance.CFrame:PointToObjectSpace(intended.Position).Y,-ray.Instance.Size.Y/2+math.abs(p1.Z-p2.Z)/2,ray.Instance.Size.Y/2-math.abs(p1.Z-p2.Z)/2),ray.Instance.CFrame:PointToObjectSpace(intended.Position).Z))
elseif kars=="x" then
print('xa')
print(ray.Instance.CFrame:PointToObjectSpace(intended.Position).Y)
intended2 = CFrame.new(Vector3.new(ray.Instance.CFrame:PointToObjectSpace(intended.Position).X,math.clamp(ray.Instance.CFrame:PointToObjectSpace(intended.Position).Y,-ray.Instance.Size.Y/2+math.abs(p1.Y-p2.Y)/2,ray.Instance.Size.Y/2-math.abs(p1.Y-p2.Y)/2),math.clamp(ray.Instance.CFrame:PointToObjectSpace(intended.Position).Z,-ray.Instance.Size.Z/2+math.abs(p1.Z-p2.Z)/2,ray.Instance.Size.Y/2-math.abs(p1.Z-p2.Z)/2)))
end
elseif f2=="x" then
intended2 = CFrame.new(Vector3.new(ray.Instance.CFrame:PointToObjectSpace(intended.Position).X,math.clamp(ray.Instance.CFrame:PointToObjectSpace(intended.Position).Y,-ray.Instance.Size.Y/2+math.abs(p1.Y-p2.Y)/2,ray.Instance.Size.Y/2-math.abs(p1.Y-p2.Y)/2),math.clamp(ray.Instance.CFrame:PointToObjectSpace(intended.Position).Z,-ray.Instance.Size.Z/2+math.abs(p1.Z-p2.Z)/2,ray.Instance.Size.Z/2-math.abs(p1.Z-p2.Z)/2)))
elseif f2=="z" then
intended2 = CFrame.new(Vector3.new(math.clamp(ray.Instance.CFrame:PointToObjectSpace(intended.Position).X,-ray.Instance.Size.X/2+math.abs(p1.X-p2.X)/2,ray.Instance.Size.X/2-math.abs(p1.X-p2.X)/2),math.clamp(ray.Instance.CFrame:PointToObjectSpace(intended.Position).Y,-ray.Instance.Size.Y/2+math.abs(p1.Y-p2.Y)/2,ray.Instance.Size.Y/2-math.abs(p1.Y-p2.Y)/2),ray.Instance.CFrame:PointToObjectSpace(intended.Position).Z))
end
intended2=CFrame.new(ray.Instance.CFrame:PointToWorldSpace(intended2.Position))
intended2=CFrame.new(intended2.Position)*CFrame.fromOrientation(jarsx,jarsy,jarsz)
game:GetService("TweenService"):Create(tb,TweenInfo.new(.1/3,Enum.EasingStyle.Quad,Enum.EasingDirection.InOut),{CFrame=intended2}):Play()
end
end
end
end)
As you can see, it’s not as simple as finding the just the measure of the area. I need to find the size vector of the surface so I can perform clamping. As you can see the code is super messy and not done yet.
Now I want you to do these things.
- Place script in your game
- Make five parts
- First part is sized (50,1,20) rotated (0,0,0)
- Second part is sized (1,50,20) rotated (0,0,90)
- Third part is sized (1,50,20) rotated (0,45,90)
- Fourth part is sized (50,20,1) rotated (85,12,2)
- Fifth part is sized and rotated whatever you want
If you can figure out how to make the building part clamped to the bounds of the surface of the part that the raycast goes to, then you have figured out the solution to my problem.
Assuming that you have the normal of the ray hit, and that the part is a block…
local function getSurfaceArea(part, surfaceNormal)
-- Get the normal relative to the part's orientation.
-- e.g. this would return (0, -1, 0) when you hit the bottom of the part, no matter how the part is oriented in the world.
local vObj = part.CFrame:VectorToObjectSpace(surfaceNormal).Unit
-- Get the absolute value of each component in the Vector, and then subtract that to (1, 1, 1) to get
-- the factor we'll multiply to the part's Size.
-- e.g. a vObj of (0, -1, 0) will translate to a factor of (1, 0, 1)
local factor = Vector3.new(1, 1, 1) - Vector3.new(math.abs(vObj.X), math.abs(vObj.Y), math.abs(vObj.Z))
-- Multiply this to the part's Size. Tada.
-- Note that a:Dot(b) == a.X * b.X + a.Y * b.Y + a.Z * b.Z
local surfaceArea = factor:Dot(part.Size)
return surfaceArea
end
Someone told me on discord to change the name of the post from Size to Area.
It seems you and the other guy misinterpreted what I wanted. I don’t want the number of the area of the size. I want the vector size.
Fair, that only requires a small alteration to the script I posted.
local function getSurfaceSize(part, surfaceNormal)
local vObj = part.CFrame:VectorToObjectSpace(surfaceNormal).Unit
local factor = Vector3.new(1, 1, 1) - Vector3.new(math.abs(vObj.X), math.abs(vObj.Y), math.abs(vObj.Z))
local surfaceSize = part.Size * factor
return surfaceSize
end
Assuming the size of the part is (2, 4, 8):
- It will return (0, 4, 8) if it hits the left or right
- It will return (2, 0, 8) if it hits the top or bottom
- It will return (2, 4, 0) if it hits the front or back
Ahh, Vector2. That’s tough, but it may be possible to resort to if-statements to convert each case to a Vector2. Does it even have to be a Vector2? Unless you’re working with UI I don’t think you’ll find it that useful here.
Confused. I’m not sure what you’re trying to do with all this information. A building system? My script gave the vector size at the beginning, called “size.” You could make the numbers in the ref table negative if you need opposite sides.
Thanks for the professionalism. I have a part that is sized (50,40,0) and is rotated (90,0,0). When I’m hovering over the top side, with normal (0,1,0), I expect the function to return (50,1,40). Instead, what it returns, is still (50,40,0).
In other words, it would be as if to lay one part on top of the face of the other, rotated (0,0,0), and sized (50,40,1), it would encompass the entire surface of the latter part which I am hovering over, yet having the rotation of (0,0,0), whereas the latter part would have the rotation of (90,0,0)
The part in red would be the part which I have given the example that it would encompass the surface my mouse would be hovering on. It has the size (50,1,40) and rotation (0,0,0) and is positioned 1 stud above the part with the decal on it.
The decal on the part with the decal on it is facing its front side, and its size is (50,40,1), and is rotated (90,0,0).
In another visual analogy, I want to find the hypothetical size of this red part with its decal facing top. which in this case, would be (50, 0.001, 40)
local function Side(CRay)
local List = {"UpVector","LookVector","RightVector"} -- Define Different Vector Directions
local List2 = {} -- Create List For Faces
local Sides = {"Top","Bottom","Front","Back","Right","Left"} -- Define Returned Value -- Change This To Modify Output
for count = 1,6,1 do
local modi = -1+(count%2)*2 -- Check if Vector Should Be Flipped
List2[CRay.Instance.CFrame[List[math.ceil(count/2)]]*modi] = Sides[count] -- Apply Vector To List
end
return List2[CRay.Normal] -- Return Hit Face Of Part
end
print(Side(--[[ Put Ray Here ]]))
I just did this
@magicalmariomario,
I tried out your script (admittedly I modified it so that it worked with raycasts)
and it doesn’t work. One example is that for the top facing surface which is the front surface of a part sized (50,40,1) and rotated (90,0,0), it returned (1,40,2), which is not what I expected, and what I expected was (50,1,40).
@dudesa3000, I have said this twice already, I don’t want to find the side name. I already know how to do that and have a function for it. I want to find the size of the side,
With the side name you can get the size all you have to do is change out the side names with vector3s and then when it returns the side multiply it by the size of the hit object
local function Side(CRay)
local List = {"UpVector","LookVector","RightVector"} -- [[Define Different Vector Directions]]
local List2 = {} -- [[Create List For Faces]]
local Sides = {{"X","Y","Z"},{"X","Z","Y"},{"Y","X","Z"}} -- [[Define Returned Value ]]-- [[Change This To Modify Output]]
for count = 1,6,1 do
local modi = -1+(count%2)*2 -- [[Check if Vector Should Be Flipped]]
List2[CRay.Instance.CFrame[List[math.ceil(count/2)]]*modi] = Sides[math.ceil(count/2)] -- [[Apply Vector To List]]
end
local Size = CRay.Instance.Size
local N = List2[CRay.Normal]
return Vector3.new(Size[N[1]],Size[N[2]],Size[N[3]]) -- [[Return Hit Face Size Of Part]]
end
print(Side(--[[ Put Ray Here ]]))
That’s not what I was looking for though, for example, the top facing face of this part,
rotated (90,0,0) and sized (85,45,1) should return (85,1,45), but using your function, would return (1,1,0), and when I multiply it with the size of that part, it would give (85,45,0), which is not what I expected.
@XAXA, any ideas here?
I am incredibly dumb. So sorry I’ll fix it
It should work now, just added a new function.
I’ll take a crack at this in a minute. Seems fun
Thanks, but I’ve encountered another problem.
print(math.clamp(ray.Instance.CFrame:PointToObjectSpace(intended.Position).X,-getSurfaceSize(ray).X/2+math.abs(p1.X-p2.X)/2,getSurfaceSize(ray).X/2-math.abs(p1.X-p2.X)/2))
print(ray.Instance.CFrame:PointToObjectSpace(intended.Position).Y)
print(math.clamp(ray.Instance.CFrame:PointToObjectSpace(intended.Position).Z,-getSurfaceSize(ray).Z/2+math.abs(p1.Z-p2.Z)/2,getSurfaceSize(ray).Z/2-math.abs(p1.Z-p2.Z)/2))
This is what I’m printing.
Now instead what I expect is
The position of the raycast to the local CFrame of the raycast instance does not return what I wanted, so I thought of the solution to make it :PointToObjectSpace() to a CFrame which is,
CFrame.lookAt(ray.Instance.Position,ray.Normal.Unit,UPVECTOR)
Now the “UPVECTOR” is what I’m trying to find. I need the upvector in order for the lookAt to work, because if a part looks like this,
and I only give it the normal vector, the CFrame might look like this
Because an Upvector hasn’t been specified,
But you can’t just find the UpVector of the raycast instance’s CFrame, because as I said, multiple parts with different rotations and sizes could look just like that big flat part in the image.
If the upvector has been specified however, the CFrame will look like this
Which is what I’m trying to solve.
The Part.CFrame.UpVector is the Normal
So whenever you perform a raycast, the only vector you’re given is the normal vector.
But I want to find the upvector of that normal vector.
Now I can’t just do “ray.Instance.CFrame.UpVector”, because sometimes the upvector of the instance could be below or to the right of the ray’s normal vector.
So what I’m trying to find is that blue axis which I have circled.