How would I make a player face the opposite direction of a wall?

What do you want to achieve?
Imagine Mario’s wall jump but in a 3D space. I need to rotate the player the same way as the direction of the wall they just landed on. Working correctly, it would look like this:

How I’m attempting to go about this is casting a ray from the character’s HumanoidRootPart, looking at the wall the player just touched, then using workspace:FindPartOnRay to get the surface normal of that wall, then orienting the HumanoidRootPart according to that surface normal.

What is the issue?

Click here to see a bunch of screenshots The current code does this. normalDirection is 0,0,0. The neon part is a visualization of the ray, you can see the code for it below.


normalDirection is -0.707079291, 0, 0.707134247

image

The ray shows a bit of deviation based on rotation, as you can see here.

The player isn’t being rotated correctly, and I’m not sure why. I think it has something to do with the ray not being cast correctly, but that wouldn’t explain why normalDirection is 0,0,0 for three of the four surfaces.

What solutions have you tried so far?
I tried looking on the developer forum but I couldn’t find anything. The code I have so far is thanks to the help of a friend helping me slowly trouble shoot, but we’re both stumped.
At first I tried orienting the character’s HumanoidRootPart opposite the wall, that didn’t work.
I’m honestly not sure what’s going wrong here, so aside from making sure I didn’t make any typos I can’t say I’ve tried “fixing” this. As far as I’m aware, this should work.

	--character.HumanoidRootPart.CFrame.p + -1*(touchedPart.CFrame.p)
	local ray = Ray.new(character.HumanoidRootPart.Position, CFrame.new(character.HumanoidRootPart.Position, touchedPart.Position).p.unit)
	local part, pointOfIntersection, normalDirection, material = workspace:FindPartOnRay(ray, player.Character)
	character.HumanoidRootPart.CFrame = CFrame.new(character.HumanoidRootPart.CFrame.p, normalDirection)
	print(normalDirection)
	
	local part = Instance.new("Part")
	part.Parent = workspace
	part.CanCollide = false
	part.Anchored = true
	part.Material = Enum.Material.Neon
	local p1 = ray.Origin
	local p2 = ray.Origin + ray.Direction*500
	part.Position = Vector3.new((p1.X+p2.X)/2, (p1.Y+p2.Y)/2, (p1.Z+p2.Z)/2)
	part.CFrame = CFrame.new(part.Position, p2)
	part.Size = Vector3.new(0.25, 0.25, (p1-p2). Magnitude)
1 Like

If you want to find directions relative to the normal you can try crossing it with the global upVector.

normalDirection:cross(Vector3.new(0,1,0))

you can also just use Angles() to rotate it.
I can show a simple example if I can set up a studio instance to see why the normal isnt accurate

1 Like

Trying this out now, will update…
As for an example, that’d be much appreciated!

2 Likes

I think you may be shooting your rays in the wrong direction.

 local ray = Ray.new(character.HumanoidRootPart.Position, CFrame.new(character.HumanoidRootPart.Position, touchedPart.Position).p.unit)

Let me just fix this clutter.

local TouchedPart
local HRP = Character.HumanoidRootPart
local Direction = CFrame.new(HRP.Position, TouchedPart.Position).Position.Unit
local ray = Ray.new(HRP.Position, Direction)

The vector Direction is the same as HRP.Position.
Direction should actually be:

local Direction = (TouchedPart.Position - HRP.Position).Unit

The CFrame you created above has the position HRP.Position and faces the direction of TouchedPart. The property you should have used is CFrame.LookVector and not CFrame.p .

2 Likes

I shot my rays to the left and right of the character and got this using the cross product for the direction:
https://i.gyazo.com/8e98f1a723048d0f6a0f000eb8a1bfb1.mp4

Not exactly sure how to implement what’s being said here… I’m very new to all of this. I changed the code to

	local HRP = character.HumanoidRootPart
	local Direction = CFrame.new(HRP.Position, TouchedPart.Position).Position.Unit
	local Direction = (TouchedPart.Position - HRP.Position).Unit
	local ray = Ray.new(HRP.Position, Direction)
	
	local Direction = (TouchedPart.Position - HRP.Position).Unit
	
	local part, pointOfIntersection, normalDirection, material = workspace:FindPartOnRay(ray, player.Character)
	character.HumanoidRootPart.CFrame = CFrame.new(character.HumanoidRootPart.CFrame.p, normalDirection)
	print(normalDirection)
	
	local part = Instance.new("Part")
	part.Parent = workspace
	part.CanCollide = false
	part.Anchored = true
	part.Material = Enum.Material.Neon
	local p1 = ray.Origin
	local p2 = ray.Origin + ray.Direction*500
	part.Position = Vector3.new((p1.X+p2.X)/2, (p1.Y+p2.Y)/2, (p1.Z+p2.Z)/2)
	part.CFrame = CFrame.new(part.Position, p2)
	part.Size = Vector3.new(0.25, 0.25, (p1-p2). Magnitude)

That allowed my rays to correctly be cast, but something is still up with how the player rotates… It’s still off. I’ll edit this post with a gif testing each wall surface and showing the result as soon as it finishes uploading.

1 Like

You can drop the first and third line that starts with “local Direction”. They do nothing.

local HRP = character.HumanoidRootPart
local Direction = (TouchedPart.Position - HRP.Position).Unit
local ray = Ray.new(HRP.Position, Direction)

This will just fix the direction you’re firing your rays so that they stop hitting nothing and causing normalDirection from being 0, 0, 0. (I hope)

Works… Kinda? I might be doing something wrong, the ray casts fine but normalDirection is still 0,0,0 sometimes.
image

1 Like

Try print out all the values you get when you do the raycast. You are probably not hitting anything when the normal is all zeros.

local part, pointOfIntersection, normalDirection, material = workspace:FindPartOnRay(ray, player.Character)
if part then
    -- We hit something!
else
    -- We didn't hit anything.
end

Yep, that’s what’s happening. Not sure why…

1 Like

On the line:

character.HumanoidRootPart.CFrame = CFrame.new(character.HumanoidRootPart.CFrame.p, normalDirection)

I believe it should be something like this:

local cf = character.HumanoidRootPart.CFrame
character.HumanoidRootPart.CFrame = CFrame.new(cf.Position, cf.Position + normalDirection)

Because the way it was set up before, your rotation would be pointed toward the center of the world since the normal vector will be in the unit sphere.

As for the raycasting issue, I believe it isn’t hitting anything because the ray is too short (looks like you’re casting with a unit length of 1, but the distance from the HumanoidRootPart to the touched part can be greater than 1 sometimes)

2 Likes

Oh damn that’s right, a ray isn’t really a ray on Roblox. It’s more of a segment.

Multiply the direction by 5 or something so it’s actually long enough to hit the wall.

1 Like

Yep, just tried this right before you mentioned it. Doing some more testing, but it looks to be working now. Not sure what to mark as the solution…

Edit: Works completely now! I’ll just make a separate post compiling everything together, obviously I’ll be sure to mention you guys!

1 Like

Post some pics/gifs when you’re satisfied with it. I’m interested in how it turns out.

Thanks to @Razorter for patiently instructing me on how to correctly cast my rays.
Thanks to @JimmyChance for reminding us that rays aren’t actually rays.
Here’s a gif of the final product! Obviously still some jank but, I think it’s working out pretty nice.

6 Likes