What do you want to achieve? I’m trying to create an Enemy NPC that pathfinds using VectorForces. The moving part of the enemy would be a sphere collider and I’d use the VectorForce to move it around. This allows me to have full control of how the enemy moves.
What is the issue? I’ve tried doing this but I’m not sure how to detect if the enemy has reached a point in the path. Also, since the function is ran every Heartbeat, the movement becomes all jittery.
What solutions have you tried so far? I tried making it repeatedly check if it reached the waypoint but it didn’t work.
This is the current code that I have for the Pathfinding. I know its pretty janky, but I don’t know what else to do here:
-- The "MoveTowardsToLoc" function sets the VectorForce' force to the direction of the Target.
local function MoveToTarget(targetPosition)
local ColPos = collider.Position
local path = PathfindingService:CreatePath({
AgentRadius = 4.742,
AgentHeight = 4.742,
})
path:ComputeAsync(ColPos, targetPosition)
if path.Status == Enum.PathStatus.Success then
CurrentPath = path
local waypoints = path:GetWaypoints()
for _, waypoint in ipairs(waypoints) do
if not Enemy.Alive then return end -- Stop movement if dead
MoveTowardsToLoc(waypoint.Position) -- Move towards each waypoint
end
else
MoveTowardsToLoc(targetPosition)
end
end
1 Like
Instead of moving directly to each waypoint in a loop, consider checking the distance to the current waypoint in your Heartbeat function. Try this
local currentWaypointIndex = 1
local function MoveToTarget(targetPosition)
local ColPos = collider.Position
local path = PathfindingService:CreatePath({
AgentRadius = 4.742,
AgentHeight = 4.742,
})
path:ComputeAsync(ColPos, targetPosition)
if path.Status == Enum.PathStatus.Success then
CurrentPath = path
local waypoints = path:GetWaypoints()
currentWaypointIndex = 1
MoveTowardsToLoc(waypoints[currentWaypointIndex].Position)
else
MoveTowardsToLoc(targetPosition)
end
end
game:GetService("RunService").Heartbeat:Connect(function()
if CurrentPath and CurrentPath.Status == Enum.PathStatus.Computing then return end
local waypoints = CurrentPath:GetWaypoints()
if currentWaypointIndex <= #waypoints then
local waypoint = waypoints[currentWaypointIndex]
local distance = (collider.Position - waypoint.Position).Magnitude
if distance < 2 then
currentWaypointIndex = currentWaypointIndex + 1
if currentWaypointIndex <= #waypoints then
MoveTowardsToLoc(waypoints[currentWaypointIndex].Position)
end
else
MoveTowardsToLoc(waypoint.Position)
end
end
end)
1 Like
I applied this to my code and it it working a bit better. For some reason, the collider is stuck moving to the first waypoint index. I don’t know why though. Here’s my code:
--// Math Functions
local v3 = Vector3.new
--// Variables
local Runservice = game:GetService("RunService")
local Model = script.Parent
local collider = Model.collider
local VectorForce = collider.VectorForce
local Gyro = collider.BodyGyro
local Config = Model.config
local MaxWander = Config.MaxWander.Value
local MaxSearchDist = Config.MaxSearchDist.Value
local Speed = Config.Speed.Value
local Friction = Config.Friction.Value
local function UpdateFriction(dt:number)
collider.Velocity = collider.Velocity - v3(collider.Velocity.X,0,collider.Velocity.Z)*Friction*dt
end
local function FindPlayer()
local col = workspace:FindFirstChild("goal")
if col and (collider.Position - col.Position).Magnitude < MaxSearchDist then
return col
end
end
local pfs = game:GetService("PathfindingService")
local WaypointIndex = 1
local CurrentPath
local LastMovePoint = v3()
local function MoveTowardsToLoc(location:Vector3)
local direction = (location - collider.Position).Unit
direction = direction * Speed
VectorForce.Force = v3(direction.X,0,direction.Z)
local ColCF = collider.CFrame
local Look = direction * v3(1,0,1)
if Look.Magnitude ~= 0 then
Gyro.CFrame = CFrame.lookAlong(ColCF.Position,direction * v3(1,0,1),ColCF.UpVector).Rotation
end
end
local function CreatePath(location : Vector3)
if CurrentPath == nil then
local ColPos = collider.Position
local Path = pfs:CreatePath()
Path:ComputeAsync(ColPos,location)
warn("Path Created: "..#Path:GetWaypoints())
CurrentPath = Path
CurrentWayPointIndex = 1
end
end
local function FollowPath()
local WayPoints : {PathWaypoint} = CurrentPath:GetWaypoints()
local CurrentPoint : PathWaypoint = WayPoints[CurrentWayPointIndex]
local ColPos = collider.Position
local Distance = (ColPos - CurrentPoint.Position).Magnitude
local MovePoint
if Distance < 2 then
CurrentWayPointIndex += 1
if CurrentWayPointIndex <= #WayPoints then
local NewPoint = WayPoints[CurrentWayPointIndex]
MovePoint = NewPoint.Position
else
warn("Path Removed")
CurrentPath = nil
CurrentWayPointIndex = nil
return
end
else
MovePoint = CurrentPoint.Position
end
if LastMovePoint ~= MovePoint then
warn("Moved: "..CurrentWayPointIndex)
MoveTowardsToLoc(MovePoint)
end
end
Runservice.PostSimulation:Connect(function(deltaTime)
local target = FindPlayer()
if target then
if CurrentPath then
if CurrentPath.Status == Enum.PathStatus.Success then
FollowPath()
else
MoveTowardsToLoc(target.Position)
end
else
CreatePath(target.Position)
end
else
print("No Target!")
end
if collider.Velocity.Magnitude ~= 0 then
UpdateFriction(deltaTime)
end
end)
I figured out a way for it to work. All I had to do is remove the Y axis of the 2 positions:
local function ry(vec:Vector3)
return vec*v3(1,0,1)
end
local function FollowPath()
local WayPoints : {PathWaypoint} = CurrentPath:GetWaypoints()
local CurrentPoint : PathWaypoint = WayPoints[CurrentWayPointIndex]
local ColPos = Enemy.collider.Position
local Distance = (ry(ColPos) - ry(CurrentPoint.Position)).Magnitude
local MovePoint
if Distance < 2 then
CurrentWayPointIndex += 1
if not (CurrentWayPointIndex > #WayPoints) then
local NewPoint = WayPoints[CurrentWayPointIndex]
MovePoint = NewPoint.Position
else
warn("Path Removed")
CurrentPath = nil
CurrentWayPointIndex = nil
return
end
else
MovePoint = CurrentPoint.Position
end
if LastMovePoint ~= MovePoint then
warn("Moved")
MoveTowardsToLoc(MovePoint)
end
end
1 Like