# Raycast hit position math

Can someone give me the correct math for cframing the part to the hit position.

I’d like to be able to move the direction like this “move(part,CFrame.new(0,0,-10))” without using LookVector.

``````local CollectionService = game:GetService('CollectionService')

function move(part,direction)
local raycastParams = RaycastParams.new()
raycastParams.FilterDescendantsInstances = {part}
raycastParams.FilterType = Enum.RaycastFilterType.Blacklist

local rayOrigin = part.CFrame.Position
local rayDestination = part.CFrame * direction.Position

local rayDirection = rayDestination - rayOrigin

local raycastResult = workspace:Raycast(rayOrigin, rayDirection,raycastParams)

if raycastResult then
--CFRAME TO HIT POSITION BUT DONT GO THROUGH WALL
else
part.CFrame *= direction
end
end

for index,part in CollectionService:GetTagged('Part') do
move(part,CFrame.new(0,0,-10))
end
end

``````

Goal: flush with the HIT target

1 Like

You can get the Raycast.Position in order to get the intersection point, then offset it with respect to the size. But I can’t really help unless I have more info, like what you are wanting in this circumstance (oriented wall/cube):

or if it is always axis-aligned.

2 Likes

I implemented my rough idea. Note I’m not really a math guy so I am not 100% sure if this is entirely optimal… but oh well. I’ll try to explain everything entirely. To begin with my implementation uses something known as “vector projection.” If you don’t know it, I would suggest either looking for a video or my poor explanation below.

Vector Projection

where vector `b` is “mapped” onto `a` in order to find `c`.

To solve for c, we notice a few things, such as how the line between the head of `b` to `c` is perpendicular to `a`, and how `c` is pretty much just a scalar multiplied by `a` (as it is the same direction, but different magnitude). These can be written as:
`c = a * t` and `(c - b):Dot(a) = 0` (a property of dot product on perpendicular vectors.)
Now the issue is solving for `t`, which if we rearrange the known formulas we can get:
`(c - b):Dot(a) = 0`
`(a * t - b):Dot(a) = 0` substitute
`a:Dot(a) * t - b:Dot(a) = 0` distribute
`a:Dot(a) * t = b:Dot(a)` add on each side
`t = b:Dot(a) / a:Dot(a)` divide on both sides
`c = a * t`

What I did first was insert the corners of the cuboid into a list,

``````local verticies = {
cframe * Vector3.new(halfSize.X, halfSize.Y, halfSize.Z),    --> right up forward
cframe * Vector3.new(halfSize.X, halfSize.Y, -halfSize.Z),   --> right up backward
cframe * Vector3.new(halfSize.X, -halfSize.Y, halfSize.Z),   --> right down forward
cframe * Vector3.new(halfSize.X, -halfSize.Y, -halfSize.Z),  --> right down backward

cframe * Vector3.new(-halfSize.X, halfSize.Y, halfSize.Z),   --> left up forward
cframe * Vector3.new(-halfSize.X, halfSize.Y, -halfSize.Z),  --> left up backward
cframe * Vector3.new(-halfSize.X, -halfSize.Y, halfSize.Z),  --> left down forward
cframe * Vector3.new(-halfSize.X, -halfSize.Y, -halfSize.Z), --> left down backward
}
``````

Then, I retrieved information from the raycast, such as the surface normal (`.Normal`) and intersection point (`.Position`). I looped through every vertex within the verticies table, and got the direction between them and the intersection. Now you may ask, “why can’t I just find the distance from the intersection and each vertex?” The reason is because what you are dealing with is finding the closest points onto a plane, not another point. It is the difference of

where the points compared to the intersection is on the left, whilst the points compared to the actual plane is on the right. This more accurately helps us find which vertex to focus on (the one that actually touches the plane without clipping and maintaining rotation) To compare the points to the plane, I projected the direction onto the surface normal, then compared its scalar value `t` to the rest in order to find both the minimum and maximum projections.

``````for i = 1, 8 do -- where 8 is the size of #verticies
local vertex = verticies[i]
local direction = vertex - intersection
--> the ratio is just `t`, or the scalar
--> multiplied by the vector meant to
--> be projected onto.
--> you may notice that it lacks
--> a divisor. this is because
--> dot product is just
--> ax * bx + ay * by + az * bz
--> so n:Dot(n) where `n` is the
--> normal and a unit vector, it
--> is simply 1. (identity property)
local scalar = direction:Dot(normal)

if scalar < minimumScalar then
minimumRatio = scalar
elseif ratio > maximumScalar then
maximumRatio = scalar
end
end
``````

An image of what you are trying to find is this:

Now you can simply compare the smallest/largest scalars.

Now you get the actual projections and get the part’s position.

``````local minimumProjection = minimumScalar * normal
local maximumProjection = maximumScalar * normal
part.Position = intersection + (maximumProjection - minimumProjection) * 0.5
``````

Also tell me if there are any issues in the math, I’ll try to fix it.

Edit: somehow forgot to add the actual code lol.

``````local part = script.Parent

local cframe = part.CFrame
local halfSize = part.Size * 0.5

local verticies = {
cframe * Vector3.new(halfSize.X, halfSize.Y, halfSize.Z),    --> right up forward
cframe * Vector3.new(halfSize.X, halfSize.Y, -halfSize.Z),   --> right up backward
cframe * Vector3.new(halfSize.X, -halfSize.Y, halfSize.Z),   --> right down forward
cframe * Vector3.new(halfSize.X, -halfSize.Y, -halfSize.Z),  --> right down backward

cframe * Vector3.new(-halfSize.X, halfSize.Y, halfSize.Z),   --> left up forward
cframe * Vector3.new(-halfSize.X, halfSize.Y, -halfSize.Z),  --> left up backward
cframe * Vector3.new(-halfSize.X, -halfSize.Y, halfSize.Z),  --> left down forward
cframe * Vector3.new(-halfSize.X, -halfSize.Y, -halfSize.Z), --> left down backward
}

local function main()
local raycast = workspace:Raycast(part.Position, Vector3.new(0, 0, 100))

if not raycast then
return
end

local intersection = raycast.Position
local normal = raycast.Normal

local minimumRatio = math.huge
local maximumRatio = -math.huge

for i = 1, 8 do
local vertex = verticies[i]
local direction = vertex - intersection
local ratio = direction:Dot(normal)

if ratio < minimumRatio then
minimumRatio = ratio
elseif ratio > maximumRatio then
maximumRatio = ratio
end
end

local minimumProjection = minimumRatio * normal
local maximumProjection = maximumRatio * normal

part.Position = intersection + (maximumProjection - minimumProjection) * 0.5
end

main()
``````
4 Likes

Wow this is a lot and I appreciate the breakdown. Any future person to come across this will be happy!

I think you provided more than what is needed… I’m actually not looking to use the ray normal cframe.

This is what I’m trying to achieve:

Maybe what I need is in the example you posted but is there a more simple way to do this? My main issue Is the part will sometimes go through the wall or go in halfway.

There probably is a much easier way I am overlooking, but the implementation I had before accounts for both cases you are talking about, and should solve them. (unless you are currently using my code in the example, if so I’ll try to figure out where I screwed up)

As for the prior explanation, let me know if you need something cleared up, I’ll try to explain to the best of my abilities.

Edit: I can’t read. I’ll think of another solution
Edit 2: To me, it looks like the video is just doing something like:

where red is the initial raycast, and blue is
`intersection + -red.Unit * part.size[FACE_RAYCAST_WAS_FIRED_OFF_OF] * 0.5`
but honestly that solution has too many edge cases I have yet to think of…
I’ll rethink it as edit1 said.