I have proceeded and done this. I have a block of code below that raycasts in a circle around the player and returns a CFrame value that is the player’s velocity mirrored along the surface of the wall, so that the two values can be interpolated, producing a singular force moving along the surface of the wall.
The function also returns a CFrame pointing opposite the direction of the wall, and a number value “force” (from 0 - 1) which represents how much force should be applied away from the wall depending on how far in the player is pointing into the wall. (i.e. if the player is moving directly at the wall, it will return 1, but if the player is approaching the wall at a slight angle, it will return 0.1 or something)
There are probably plenty of things about this I could clean up, but here’s the function as-is if anybody in the future needs it. I’ve commented it best I could, but even so I wish you the best of luck navigating it.
-- RadialDetect is a function that circles rays about the player in all directions until it locates a wall, returns the direction of the surface
local function RadialDetect(detail, moveDir2)
local charPos = PhysicsRoot.Position
local moveDir = CFrame.new(charPos, charPos + moveDir2)
--loop as many times as stated
for i = 1, detail do
--Create a ray along the designated interval
local degrees = ((i-1)/detail ) * 360
local RayCF = CFrame.fromOrientation(0, math.rad(degrees), 0) + charPos
local raycast = Ray.new(charPos, RayCF.LookVector * (PhysicsRoot.Size.X/2 + 0.01) )
--look for parts
local found = false
for _, k in pairs(GetAllParts(raycast, Char)) do
if k.Parent ~= Char and k.CanCollide then
found = k
end
end
--found a part
if found then
--Determine which direction is closer to the direction the player is moving in
local px, py, pz = moveDir:ToOrientation()
local y1 = degrees + 90
local y2 = degrees - 90
--Determine the angle away from the wall (for use later)
local away = degrees - 180
if away < 0 then away = away + 360 end
away = CFrame.fromOrientation(0, math.rad(away), 0)
--format angle into its positive variant
if py < 0 then
py = py + math.rad(360)
end
--If player was moving more left, send them that way. Otherwise send them right
if math.abs(y1 - math.deg(py)) <= math.abs(y2 - math.deg(py)) then
--d1 is the reflected angle
local d1 = 2 * math.rad(y1) - py
--force determines how strong the push away from the wall should be
local force = math.sin(py - math.rad(y1))
--check for NaN errors
if force ~= force then force = nil end
--If d1 sends them into the wall, return nil so their movement is not affected
if py < d1 then
return CFrame.fromOrientation(0, d1, 0), away, force
else
return nil, away, force
end
else
--d1 is the reflected angle
local d1 = 2 * math.rad(y2) - py
if y2 < 0 then
y2 = y2 + 360
end
--force determines how strong the push away from the wall should be
local force = math.sin(py - math.rad(y2))
--check for NaN errors
if force ~= force then force = nil end
--if d1 sends them into the wall, return nil so their movement is not affected
if py > d1 then
return CFrame.fromOrientation(0, d1, 0), away, force
else
return nil, away, force
end
end
end
end
--nothing was found, dont change their movement
return nil
end
This method has produced the following result:

Its not perfect but at this point I think I can live with that.