I am currently working on a class-based fighting game similar to Brawl Stars where one of the things players can do is spawn NPCs to help join the fight. The NPCs must be capable of traversing the maps which will be vertically complex and large. However when it comes time to jump or climb, it goes well at first and then completely breaks. I have been completely unable to find a fix for the issue, either by myself or on the DevForum.
robloxapp-20230625-1451459.wmv (3.6 MB)
In this video is a demonstration of how it breaks. The red balls represent waypoints reached by walking, and the yellow balls represent waypoints reached by jumping. It appears to form two waypoints bunched up together (its like this no matter how spaced the waypoints are, or no matter how much I edit the agent parameters) and it seems to be attempting to reach one of these bunched up waypoints first before proceeding with the jump or climb.
If the NPC fails a jump, or if I move it to the ground after it reaches the top, then the same thing will happen where it will be unable to jump up because it seems to be trying to reach a second waypoint. This time however, the second waypoint will sometimes be where the jump point should be, and it will then start attempting to walk up the block instead of jumping (which is impossible) as seen below:
robloxapp-20230625-1504008.wmv (5.5 MB)
Once that happens, I cannot get it to stop trying. Even with the path constantly recalculating so it can follow me, it is determined to walk up that block as seen with the red coloured ball. It sees it as a 100% necessary step to reaching me no matter where I am. The only way I can break its determination is to move the block.
Below is the code used by the NPC to find a target and begin pathfinding. Its basically a copy-paste from the official roblox documentation on pathfinding, except its been heavily modified to fit my needs.
---VARIABLES----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
PLAYER_SERVICE = game:GetService("Players")
PATHFINDING_SERVICE = game:GetService("PathfindingService")
NPC = script.Parent
MAX_RANGE = script.Parent.CONFIGURE.MAX_CHASE_RANGE.Value
LOOP_TIME = 0.1 -- How Often Path Is Calculated.
---FUNCTIONS----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
PATH = PATHFINDING_SERVICE:CreatePath({
AgentRadius = 3,
AgentHeight = 5,
AgentCanJump = true,
AgentCanClimb = true,
WaypointSpacing = 50,
Costs = {
Ice = 10,
Water = 20,
Climb = 5,
DANGER_ZONE = 100,
DEADLY_ZONE = math.huge
}
})
local WAYPOINTS
local NEXT_WAYPOINT_INDEX
local BLOCKED_CONNECTION
local REACHED_CONNECTION
function FOLLOW_PATH(DESTINATION)
local SUCCESS, ERROR = pcall(function() -- Compute The Path.
PATH:ComputeAsync(NPC.HumanoidRootPart.Position, DESTINATION.Position)
end)
if SUCCESS and PATH.Status == Enum.PathStatus.Success then
WAYPOINTS = PATH:GetWaypoints() -- Get The Path's Waypoints.
for C,nms in pairs (script.Parent.nms:GetChildren()) do -- TEMPORARY, JUST FOR DEBUGGING!
nms:Destroy()
end
local old_nm
for C, WAYPOINT in pairs (WAYPOINTS) do -- TEMPORARY, JUST FOR DEBUGGING!
local nm = game.Workspace.WP:Clone()
if WAYPOINT.Action == Enum.PathWaypointAction.Jump then
nm.BrickColor = BrickColor.new("New Yeller")
end
if old_nm ~= nil then
nm.Beam.Attachment1 = old_nm.Attachment
end
nm.Position = WAYPOINT.Position
nm.Parent = script.Parent.nms
old_nm = nm
end
BLOCKED_CONNECTION = PATH.Blocked:Connect(function(BLOCKED_WAYPOINT_INDEX) -- Detect If Path Becomes Blocked.
if BLOCKED_WAYPOINT_INDEX >= NEXT_WAYPOINT_INDEX then -- Detect If Blockage Is Futher Down The Path.
BLOCKED_CONNECTION:Disconnect() -- Stop Detecting Until Path Is Re-Computed.
FOLLOW_PATH(DESTINATION) -- Re-Compute The Path.
end
end)
if not REACHED_CONNECTION then -- Detect When Movement To Next Waypoint Is Complete.
REACHED_CONNECTION = NPC.Humanoid.MoveToFinished:Connect(function(REACHED)
if REACHED and NEXT_WAYPOINT_INDEX < #WAYPOINTS then -- Increase Waypoint Index & Move To The Next Waypoint.
NEXT_WAYPOINT_INDEX += 1
NPC.Humanoid:MoveTo(WAYPOINTS[NEXT_WAYPOINT_INDEX].Position)
if WAYPOINTS[NEXT_WAYPOINT_INDEX].Action == Enum.PathWaypointAction.Jump then -- Jump If Required.
NPC.Humanoid.Jump = true
end
else
REACHED_CONNECTION:Disconnect()
BLOCKED_CONNECTION:Disconnect()
end
end)
end
NEXT_WAYPOINT_INDEX = 2
NPC.Humanoid:MoveTo(WAYPOINTS[NEXT_WAYPOINT_INDEX].Position)
end
end
---FUNCTIONALITY------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
wait(0.25) -- Allow NPC To Load In First, Just To Be Safe.
while true do -- Scan For Nearest Humanoid, And Create A Path To Them.
wait(LOOP_TIME)
local NEAREST_DISTANCE = math.huge
local NEAREST_HUMANOID
for COUNT, TARGET in ipairs(game.Workspace.WORLD_CONFIGURATION.WORLD_DEBRIS.HUMANOIDS:GetChildren()) do
local TARGET_HUMANOID = TARGET:FindFirstChild("Humanoid")
local TARGET_HRP = TARGET:FindFirstChild("HumanoidRootPart")
if TARGET:IsA("Model") and TARGET_HUMANOID and TARGET_HRP and TARGET_HUMANOID.Health > 0 and TARGET ~= script.Parent then
local PLAYER = PLAYER_SERVICE:GetPlayerFromCharacter(TARGET)
local DISTANCE = (NPC.Head.Position - TARGET_HRP.Position).magnitude
if DISTANCE <= MAX_RANGE and DISTANCE < NEAREST_DISTANCE then
if PLAYER then -- Dont Mark Players/NPCs Of The Same Team.
if PLAYER.TeamColor ~= NPC.NPC_TEAM.Value then
NEAREST_HUMANOID = TARGET
NEAREST_DISTANCE = DISTANCE
end
else
if TARGET.NPC_TEAM.Value ~= NPC.NPC_TEAM.Value then
NEAREST_HUMANOID = TARGET
NEAREST_DISTANCE = DISTANCE
end
end
end
end
end
if NEAREST_HUMANOID ~= nil then -- If Humanoid Is In Range.
local PLAYER = PLAYER_SERVICE:GetPlayerFromCharacter(NEAREST_HUMANOID)
if PLAYER ~= nil then -- Humanoid Is A Player.
if PLAYER.TeamColor ~= NPC.NPC_TEAM.Value and NPC.Humanoid.Health > 0 and PLAYER.Character.Humanoid.Health > 0 then
FOLLOW_PATH(NEAREST_HUMANOID:FindFirstChild("HumanoidRootPart"))
end
elseif PLAYER == nil then -- Humanoid Is An NPC.
if NEAREST_HUMANOID.NPC_TEAM.Value ~= NPC.NPC_TEAM.Value and NPC.Humanoid.Health > 0 and NEAREST_HUMANOID.Humanoid.Health > 0 and NEAREST_HUMANOID.STATUS_EFFECTS.INVISIBLE.Value == false then
FOLLOW_PATH(NEAREST_HUMANOID:FindFirstChild("HumanoidRootPart"))
end
end
end
end
---END----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
Ive been stuck on this problem for two days now, any help would be greatly appreciated!