How to use CFrame.LookAt While Maintaining a Part's UpVector on a Slope?

The green part needs to be flat on the slope at all times. But it also needs to face towards the red part which can be at any height above or below the green part.

The green part is raycasted onto the slope, so you can use Raycast.Position and Raycast.Normal.

1 Like

It’s called projection. Let’s remove the local height value of the red brick by doing:

local position = red.Position
local delta = position - Raycast.Position
position -= delta * Raycast.Normal:Dot(delta.Unit)

green.CFrame = CFrame.new(Raycast.Position, position)

Let me know if works :slight_smile:

It’s very close, but the orientation of the UpVector isn’t working. It does not appear to change with the normal.

Right! Lookat wont rotate the brick correctly.

Try

local position = Raycast.Position
local upVector = Raycast.Normal
local lookVector = (red.Position - position).Unit

lookVector = (lookVector - lookVector:Dot(upVector) * upVector).Unit

local rightVector = lookVector:Cross(upVector)

green.CFrame = CFrame.fromMatrix(position, rightVector, upVector, -lookVector)

Closer, but still not quite right. Take a look at this video:

https://gyazo.com/d63c49bc3e6bfd8ebfe896e36d0f707f

The “red brick” in this video is the Player’s Camera CFrame.
It doesn’t look like it’s always facing the camera, but it is staying level with the normal now.

so the green is suppose to face the camera at all times?

Yes, it needs to face the camera while remaining flat on the normal.

How about this?

local position = Raycast.Position
local upVector = Raycast.Normal
local lookVector = workspace.CurrentCamera.CFrame.lookVector

local rightVector = lookVector:Cross(upVector)
lookVector = upVector:Cross(rightVector)

green.CFrame = CFrame.fromMatrix(position, rightVector, upVector, -lookVector)
2 Likes

Bless you, that is what I have been looking for. Thanks a ton.

1 Like

Do you know how I can prevent the position from going “behind” the camera? It seems to happen whenever the angle of the camera and the slope is <90 degrees, such as looking straight down and uphill while on a slope.

Video: https://gyazo.com/9b0a553a2d2eb0672f2616f1920fe6a2

I feel like this would only cause more wierd behavior, but you could try add

local camCFrame = workspace.CurrentCamera.CFrame
local lookVector
if camCFrame.UpVector.Y < 0 then
lookVector = camCFrame.UpVector
else
lookVector  = camCFrame.lookVector
end

This can be greatly simplified if you use CFrame.lookAt, which accepts 3 vectors.

green.CFrame = CFrame.lookAt(Raycast.Position, red.Position, Raycast.Normal)
1 Like

This does not work. It was the first thing I tried.

My bad, I misread the thread and thought the issue was something else

I’ve also noticed that standing parallel to the slope it doesn’t quite line up the way I’d expect it to like it does while facing up or down the slope. Not related to my clamping in the code below, already checked that.

local Placement = Deployable.Placement
local Bounds = Placement.PrimaryPart
---
local CameraCFrame = self.CurrentCamera.CFrame
local X,Y,Z = CameraCFrame:ToOrientation()
X = math.clamp(X-math.rad(20), math.rad(-85), math.rad(-40))
CameraCFrame = CFrame.new(CameraCFrame.Position) * CFrame.fromOrientation(X, Y, 0)
local RayOrigin = CameraCFrame.Position
local RayLookVector = CameraCFrame.LookVector
---
local RayDirection = RayLookVector*10
local Raycast = workspace:Raycast(RayOrigin, RayDirection, RayParams)
local Position = (Raycast and Raycast.Position) or (RayOrigin + RayDirection)
local Normal = (Raycast and Raycast.Normal) or Vector3.yAxis
---
local RightVector = RayLookVector:Cross(Normal)
local LookVector = Normal:Cross(RightVector)
local Pivot = CFrame.fromMatrix(Position, RightVector, Normal, -LookVector)
local Extents = Placement:GetExtentsSize()
Bounds.PivotOffset = CFrame.new(0,-Extents.Y/2,Extents.Z/2)
Placement:PivotTo(Pivot)

I tried adjusting the LookVector like you recommended and it didn’t seem to work, but I could have implemented it wrong.

1 Like

I can confirm after testing the slight misalignment is related to the height of the camera. It becomes more pronounced the higher the camera is.

Fixed the misalignment by doing:

local Test = Vector3.new(RayOrigin.X, Position.Y, RayOrigin.Z)
RayLookVector = (Test - Position).Unit

Now that I’m testing this, this change fixed the behind camera / <90 issue as well @StonksMcHat

1 Like