local unitRay = camera:ViewportPointToRay(position.X, position.Y)
local ray = Ray.new(unitRay.Origin, unitRay.Direction * LENGTH)
local hitPart, worldPosition = workspace:FindPartOnRay(ray)
-- To get where the touch input is in the world.
local function round(vector,grid)
-- I use this to make later make the targeted position snapped into a 2x2 grid.
local ElVecoro = round(Vector3.new(newWorldPosition.X, newWorldPosition.Y, newWorldPosition.Z), 2)
SelectedPart.Position = ElVecoro
-- then this to set the position
RaycastResults have a normal property, so try using workspace:Raycast instead of workspace:FindPartOnRay. Usage is essentially the same, just that workspace:Raycast has an optional RaycastParams parameter.
By surface, you mean like left, right, top, bottom etc, right? If so, that’s what the normal property is (eg. workspace:Raycast(…).Normal). The thing is, though, that the property is a Vector3 so you’d have to use this to determine the normal.
0,1,0 = top
0, -1, 0 = bottom
-1, 0, 0 = left
1, 0, 0 = right
0, 0, -1 = front
0, 0, 1 = back
If you want a more algorithmic approach you could do something like:
local function getNormalIdFromVector(vector: Vector3) -- vector is the RaycastResult.Normal
for i, normalId in ipairs(Enum.NormalId:GetEnumItems()) do
if Vector3.fromNormalId(normalId) == vector then
^ That should work, but if you encounter floating point issues (not sure if they are prominent with RaycastResult normals) you can check the magnitude between the two vectors is less than some low number like 0.005 or so.
Unless you mean the position where the ray hits the part, that would be the RaycastResult.Position.