Pathfinding doesnt want to drop from "high" places

I’ve been trying to script a stupid pathfinding system for like 10 hours now, and I’m getting somewhat close.

Right now I’m using the script from the Character Pathfinding documentation, and I’m having problems making the NPC pathfinding go down from stuff

local char = script.Parent;
local root = char:WaitForChild("HumanoidRootPart");
local torso = char:WaitForChild("Torso");

local hum = char:WaitForChild("Humanoid");
local statusEffects = hum:WaitForChild("StatusEffects");
local health = hum:WaitForChild("PlayerHealth");
local armor = hum:WaitForChild("PlayerArmor");
local state = hum:WaitForChild("PlayerState");

---

local npcName = char.Name;
local startingPos = root.Position;

local jumpTick = tick();
local wanderTick = 0;

---

local pathfindingService = game:GetService("PathfindingService");
local runService = game:GetService("RunService");

local getSortedTargets = require(game.ReplicatedStorage.Functions.NPCs.Functions.GetSortedTargets);
local runAdjustors = require(game.ReplicatedStorage.Functions.Misc.RunAdjustors);
local getIgnores = require(game.ReplicatedStorage.Functions.Misc.GetRaycastIgnores);

-----

local loadout = {	
	alive = false;
	stunned = false;

	target = nil;
	lastSeenTargetPosition = nil;
	
	range = 0;
	damage = 0;
	walkspeed = 0;
	jumppower = 0;
};

---

local charSize = char:GetExtentsSize();

local pathParams = {
	WaypointSpacing = math.huge;

	AgentRadius = (charSize.X+charSize.Z)/4;
	AgentHeight = charSize.Y;
	AgentCanJump = true;

	Costs = {
		Water = 10;
	};
};

-----

function softWait(goal)
	local i = 0;
	local target = nil;
		
	repeat 
		wait(0.1);
		i += 0.1;
		
		local targets = getSortedTargets.execute(char, loadout);
		local target = targets[1];
		
	until i >= goal or state.Value == "Aggrovated" or target;
end;

function stopExisting()
	loadout.alive = false;
	
	state.Value = "GameOver";
	
	wait();
	
	---
	
	game.ReplicatedStorage.Events.SpawnRagdoll:Invoke(char);
	
	---
	
	char:Destroy();
end;

function healthChanged()
	if	(health.Value <= 0)	then
		stopExisting();
	else
		state.Value = "Aggrovated";
	end;
end;

function armorChanged()
	state.Value = "Aggrovated";
end;

function changeState()	
	local statename = state.Value;

	local walkspeed = loadout.walkspeed;
	local jumppower = loadout.jumppower;

	local statset = runAdjustors.execute(char, {
		["STAT_MovementHindered"] = false;
	}, nil, "AdjustStats");
	
	if	(statename == "GameOver")	then
		walkspeed = 0;
		jumppower = 0;
	end;
	
	if	(statset["STAT_MovementHindered"])	then
		walkspeed *= 0.5;
		jumppower = 0;
	end;
	
	hum.WalkSpeed = walkspeed;
	hum.JumpPower = jumppower;
end;

function loadNPC()
	local npcset = game.ReplicatedStorage.NPCs:FindFirstChild(npcName);
	
	if	(npcset)	then
		npcset = require(npcset);
		npcset = runAdjustors.execute(char, npcset, nil, "AdjustStats");
		
		local npcstats = npcset.NpcStats;

		health.Max.Value = npcstats["NPC_Health"];
		health.Value = health.Max.Value;

		armor.Max.Value = npcstats["NPC_Armor"];
		armor.Value = armor.Max.Value;
		
		---

		loadout.viewdistance = npcstats["NPC_ViewDistance"];
		loadout.attackrange = npcstats["NPC_AttackRange"];
		loadout.damage = npcstats["NPC_Armor"];
		loadout.walkspeed = npcstats["NPC_WalkSpeed"];
		loadout.jumppower = npcstats["NPC_JumpPower"];

		loadout.agentRadius = npcstats["AgentRadius"];
		loadout.agentHeight = npcstats["AgentHeight"];
		
		---
		
		state.Value = "Idle";
		
		---
		
		loadout.alive = true;
	end;
end;

function setNewTarget(target)
	if	(target)	then
		loadout.target = {
			Character = target;
			Humanoid = target:FindFirstChild("Humanoid");
			Root = target:FindFirstChild("HumanoidRootPart");
		};
	else
		loadout.target = nil;
	end;
end;

---

local waypoints;
local nextWaypointIndex;
local reachedConnection;
local blockedConnection;
local path = pathfindingService:CreatePath(pathParams);
local ignore = getIgnores.execute("workspace", true, true, true, false, true);

local function followPath(destination)
	local success, errorMessage = pcall(function()
		path:ComputeAsync(root.Position, destination)
	end)

	if success and path.Status == Enum.PathStatus.Success then
		local waypoints = path:GetWaypoints()

		blockedConnection = path.Blocked:Connect(function(blockedWaypointIndex)
			if blockedWaypointIndex >= nextWaypointIndex then
				blockedConnection:Disconnect()
				followPath(destination)
			end
		end)

		-- Detect when movement to next waypoint is complete
		if not reachedConnection then
			reachedConnection = hum.MoveToFinished:Connect(function(reached)
				if reached and nextWaypointIndex < #waypoints then
					-- Increase waypoint index and move to next waypoint
					nextWaypointIndex += 1
					hum:MoveTo(waypoints[nextWaypointIndex].Position)
				else
					reachedConnection:Disconnect()
					blockedConnection:Disconnect()
				end
			end)
		end

		nextWaypointIndex = 2
		hum:MoveTo(waypoints[nextWaypointIndex].Position);
		
		local obstacleRay = Ray.new(root.Position, root.CFrame.LookVector * (loadout.agentRadius+1));
		local hit = game.Workspace:FindPartOnRayWithIgnoreList(obstacleRay, ignore);
		if	(hit)	then

			if	((tick()-jumpTick) > 1)	then
				hum.Jump = true;
				jumpTick = tick();
			end;

		end;
		
		hum.MoveToFinished:Wait();
	else
		hum:MoveTo(destination);
		hum.MoveToFinished:Wait();
	end
end;

loadNPC();

-----

health.Changed:Connect(healthChanged);
armor.Changed:Connect(armorChanged);
hum.Died:Connect(stopExisting);

---

changeState();
state.Changed:Connect(changeState);

statusEffects.ChildAdded:Connect(function()
	changeState();
end);

statusEffects.ChildRemoved:Connect(function()
	changeState();
end);

-----

local destination = nil;
local hInt = 0;
runService.Heartbeat:Connect(function()
	hInt += 1;

	if	(not loadout.alive)	then return end;
	if	(hInt < 10)	then return end;
	
	hInt = 0;
	if	(char and hum)	then

		local targets = getSortedTargets.execute(char, loadout);
		local targetData = targets[1];
		if	(targetData)	then
			if	(not loadout.target or loadout.target.Character ~= targetData.key)	then
				setNewTarget(targetData.key);
			end;

			loadout.lastSeenTargetPosition = loadout.target.Root.Position;
			state.Value = "Aggrovated";

		else
			setNewTarget(nil);
			state.Value = "Idle";

		end;

		---

		if	(loadout.target)	then
			destination = loadout.target.Root.Position;

		elseif	(loadout.lastSeenTargetPosition)	then

			if	((root.Position - loadout.lastSeenTargetPosition).Magnitude < 25)	then
				loadout.lastSeenTargetPosition = false;
			else
				destination = loadout.lastSeenTargetPosition;
			end;
			
		else
			if	((tick()-wanderTick)>5)	then
				destination = startingPos + Vector3.new(math.random(-25,25), 0, math.random(-25,25));
				wanderTick = tick();
			end;

		end;

		if	(destination)	then
			followPath(destination);
		end;
	end;
end);

I think I’ve looked through like 30 different threads on pathfinding shenanigans, but I can’t find anything on this issue.

Can anyone tell me what might be wrong, or point me to some direction?

I tried offseting the Y axis of the destination by -2, thinking the path finding is trying to reach something in mid air, but that didn’t seem to work

solved with the help of

1 Like