I’m trying to make it so when an NPC can’t move back anymore that it should move along an invisible barrier. Here is a picture of what I mean:

The road block preventing me from figuring this out is that NPC is always facing the player and along with my poor knowledge of CFrame, can’t figure out how to always get it to move along a wall no matter the direction the NPC faces.
Any advice and help is greatly appreciated, thank you.
6 Likes
You can use the Humanoid’s :MoveTo() method to walk to a given point along the side of the wall, and make sure that AutoRotate is set to false on the humanoid to prevent the NPC turning while walking in this direction.
If you need to rotate the character to continue facing a point even while moving along the wall, then you can use CFrame.new(pos, lookAt) to make a new CFrame for the NPC looking at a position. I’d also note that there is a PathfindingService which if will return a list of points you walk along if you supply a point to move towards.(which may not suit your purposes if you just want to move away from a player…)
2 Likes
Thank you for the response, the problem that I was having was determining a given point along a wall. I just ended up putting some parts along each wall and then having the npc move to the closest one.
1 Like
Ah, okay. What I’d do is convert the NPC’s position to the wall’s local coordinates. This makes it easy to determine which face of the wall is the one the NPC is running into. Once you know which face they are running into, you can convert a vector along that surface to world space and your have the direction to travel! This is what it would look like:
local INSIDE_ERROR_MESSAGE = 'The given point is inside the wall'
local EDGE_ERROR_MESSAGE = 'The given point is not infront of a wall, but near a edge. Cannot tell which of two walls was hit.'
local CORNER_ERROR_MESSAGE = 'The given point is not infront of a wall, but near a corner. Cannot tell which of three walls was hit'
local xUnitVector = Vector3.new(1, 0, 0)
local yUnitVector = Vector3.new(0, 1, 0)
local zUnitVector = Vector3.new(0, 0, 1)
local function getSideDirections(npcPos, wall)
-- Each surface has two vectors along it, one is up/down (y) and one is left/right (x)
-- We'll need to decide later which is which
local possibleVector1, possibleVector2
-- To detect that we are infront of a surface, we should be in the bounds set by the size of
-- the part in two axis's. Those two axis's are also the possible side directions to move
-- along the side of the wall. (We wouldn't want to use the third axis and move into or away
-- from the wall.)
local bounds = wall.Size/2
local localPos = wall.CFrame:pointToObjectSpace(npcPos)
local inXBounds = localPos.X > -bounds.X and localPos.X < bounds.X
local inYBounds = localPos.Y > -bounds.Y and localPos.Y < bounds.Y
local inZBounds = localPos.Z > -bounds.Z and localPos.Z < bounds.Z
if inXBounds then
possibleVector1 = xUnitVector
end
if inYbounds then
if possibleVector1 then
possibleVector2 = yUnitVector
else
possibleVector1 = yUnitVector
end
end
if inZBounds then
if not possibleVector1 then
-- Only 1 axis in bounds (z)
error(EDGE_ERROR_MESSAGE)
elseif not possibleVector2 then
possibleVector2 = zUnitVector
else
-- All axis's in bounds
error(INSIDE_ERROR_MESSAGE)
end
elseif not possibleVector1 then
-- No axis's in bounds
error(CORNER_ERROR_MESSAGE)
elseif not possibleVector2 then
-- Only 1 axis in bounds (x or y)
error(EDGE_ERROR_MESSAGE)
end
-- Determine which vector seems like it is up/down, then pick the left/right to move along.
possibleVector1 = wall.CFrame:vectorToWorldSpace(possibleVector1).Unit
possibleVector2 = wall.CFrame:vectorToWorldSpace(possibleVector2).Unit
if math.abs(possibleVector1:Dot(yUnitVector)) > math.abs(possibleVector2:Dot(yUnitVector)) then
-- possibleVector1 is up/down
return possibleVector2, -possibleVector2
else
-- possibleVector2 is up/down
return possibleVector1, -possibleVector1
end
end
Now just add the returned vector to the NPC’s position and you have a point to go to 1 stud away from the NPC (vectors returned are units).
1 Like
Which return vector do we use? You return 2 of them.
Edit: Oh never mind, I see what you did. You returned 2 vectors, one to go left and the other to go right.
If you want to determine which moves further from the player faster then use this function:
local function farthestFrom(vector, option1, option2)
if vector:Dot(option1) < vector:Dot(option2) then
return option1
else
return option2
end
end
Where ‘vector’ is the vector from the NPC to the player (playerPos - npcPos) and option1/2 are the vectors returned by my previous function (left and right).
1 Like