Skip to content

Latest commit

 

History

History
169 lines (128 loc) · 4.21 KB

File metadata and controls

169 lines (128 loc) · 4.21 KB

@rbxts/path

A TypeScript wrapper for Roblox's PathfindingService with async/await support, robust error handling, and a built-in Finite State Machine (FSM) for advanced path state management. Includes utilities for pathfinding, waypoint manipulation, and a SmartPath class with automatic retry and recovery.

Features

  • Async/await path computation
  • Finite State Machine for path state transitions and events
  • SmartPath: automatic retry and recovery on failure/blockage
  • Utility functions for distance, waypoints, and performance estimation
  • TypeScript types for all core concepts

Installation

npm i @rbxts/path

Usage

Basic Pathfinding

const path = new Path();

async function computePath() {
	const status = await path.computeAsync(startPos, endPos);

	if (status === PathStatus.Success) {
		const waypoints = path.getWaypoints();
		if (!waypoints || waypoints.size() === 0) {
			print("No waypoints found in the computed path.");
			return;
		}
		for (const waypoint of waypoints) {
			const targetPos = waypoint.position;
			humanoid.MoveTo(targetPos);

			const moveStatus = await new Promise<boolean>((resolve) => {
				const connection = humanoid.MoveToFinished.Connect((reached) => {
					connection.Disconnect();
					resolve(reached);
				});
			});
			if (!moveStatus) {
				print(`Failed to move to waypoint at ${targetPos}`);
				return;
			}
		}
	}

	path.destroy();
}

computePath();

Using the Finite State Machine

import { Path, PathState, PathStatus } from "@rbxts/path";

const path = new Path(
	{},
	{
		onStateChanged: (oldState, newState) => print(`State changed: ${oldState}${newState}`),
		onWaypointReached: (waypoint, index) => print(`Reached waypoint ${index}`),
		onPathCompleted: () => print("Path completed successfully!"),
		onPathFailed: (reason) => warn(`Path failed: ${reason}`),
		onPathBlocked: () => warn("Path blocked!"),
	},
);

async function computePathWithFSM() {
	const status = await path.computeAsync(startPos, endPos);

	if (status === PathStatus.Success) {
		path.startFollowing(humanoid);

		while (path.getState() === PathState.Following) {
			task.wait(0.1);
		}

		const finalState = path.getState();
		if (finalState === PathState.Completed) {
			print("Successfully reached destination!");
		} else if (finalState === PathState.Failed) {
			print("Path following failed");
		} else if (finalState === PathState.Blocked) {
			print("Path was blocked");
		}
	}

	path.destroy();
}

async function advancedExample() {
	await path.computeAsync(startPos, endPos);

	if (path.isSuccess()) {
		path.startFollowing(humanoid);

		task.wait(3);
		path.pause();
		print(`Paused - State: ${path.getState()}`);

		task.wait(2);
		path.resume(humanoid);
		print(`Resumed - State: ${path.getState()}`);
	}
}

computePathWithFSM();

SmartPath: Automatic Retry

import { SmartPath, PathStatus } from "@rbxts/path";

async function smartPathExample() {
	const smartPath = new SmartPath(
		{},
		{},
		{
			maxRetries: 5,
			retryDelay: 1,
			useExponentialBackoff: true,
		},
	);

	const status = await smartPath.computeAsync(startPos, endPos);

	if (status === PathStatus.Success) {
		smartPath.startFollowing(humanoid);
		print("SmartPath started - will auto-retry if blocked!");
	} else {
		print(`Initial path computation failed: ${status}`);
	}
}

smartPathExample();

Utilities

import { PathfindingUtils } from "@rbxts/path";

const exists = await PathfindingUtils.pathExists(start, finish);
const distance = await PathfindingUtils.getPathDistance(start, finish);
const waypoints = await PathfindingUtils.getWaypoints(start, finish);

API Reference

License

MIT

Contributions

Contributions are welcome (and encouraged)! Please feel free to submit a Pull Request. I am newer to rbxts package creation and would appreciate all recommendations!