--- title: NPC Movement type: docs weight: 4 --- The NPC movement system handles navigation, pathfinding, and motion control for NPCs. **Packages:** - `com.hypixel.hytale.server.npc.movement` - `com.hypixel.hytale.server.npc.navigation` ## Motion Controller The `MotionController` executes movement commands and manages NPC locomotion. ### MotionController Class ```java public class MotionController { private NPCEntity npc; private MovementState state; private float currentSpeed; private Vector3d targetVelocity; // Movement commands public void moveTo(Vector3d target); public void moveInDirection(Vector3d direction); public void stop(); // Speed control public void setSpeed(float speed); public void walk(); public void run(); public void sprint(); // State queries public boolean isMoving(); public boolean hasReachedTarget(); public MovementState getState(); } ``` ### Movement States ```java public enum MovementState { IDLE, // Not moving WALKING, // Normal movement RUNNING, // Fast movement SPRINTING, // Maximum speed JUMPING, // In air (jump) FALLING, // In air (fall) SWIMMING, // In water CLIMBING, // On ladder/vine SLIDING // On slope } ``` ### Using MotionController ```java NPCEntity npc = // get NPC MotionController motion = npc.getMotionController(); // Move to position motion.moveTo(targetPosition); // Set movement speed motion.run(); // or motion.setSpeed(5.0f); // Check if arrived if (motion.hasReachedTarget()) { // Destination reached } // Stop movement motion.stop(); ``` ## Path Follower The `PathFollower` tracks and follows calculated paths. ### PathFollower Class ```java public class PathFollower { private List path; private int currentIndex; private float waypointRadius; private boolean smoothPath; // Path management public void setPath(List path); public void clearPath(); public boolean hasPath(); // Following public Vector3d getNextWaypoint(); public void advanceToNextWaypoint(); public boolean hasReachedWaypoint(Vector3d position); // Progress public float getPathProgress(); // 0.0 to 1.0 public int getRemainingWaypoints(); } ``` ### Path Following Example ```java PathFollower pathFollower = npc.getPathFollower(); MotionController motion = npc.getMotionController(); // Set a path pathFollower.setPath(calculatedPath); // In update loop if (pathFollower.hasPath()) { Vector3d nextWaypoint = pathFollower.getNextWaypoint(); // Move towards waypoint motion.moveTo(nextWaypoint); // Check if reached if (pathFollower.hasReachedWaypoint(npc.getPosition())) { pathFollower.advanceToNextWaypoint(); } } ``` ## Navigation Graph The navigation system uses A* pathfinding on a navigation graph. ### NavigationGraph Class ```java public class NavigationGraph { // Find path between points public List findPath( Vector3d start, Vector3d end, NavigationConfig config ); // Check if point is navigable public boolean isNavigable(Vector3d position); // Get nearest navigable point public Vector3d getNearestNavigablePoint(Vector3d position); } ``` ### NavigationConfig ```java public class NavigationConfig { private float maxDistance; // Maximum path length private float stepHeight; // Max step up height private float entityWidth; // Entity collision width private float entityHeight; // Entity collision height private boolean canSwim; // Allow water paths private boolean canClimb; // Allow ladder/vine paths private boolean canOpenDoors; // Allow door traversal private Set avoidBlocks; // Blocks to avoid // Builder pattern public static NavigationConfig builder() .maxDistance(100.0f) .stepHeight(1.0f) .entityWidth(0.6f) .entityHeight(1.8f) .canSwim(false) .build(); } ``` ### Pathfinding Example ```java NavigationGraph navGraph = world.getNavigationGraph(); NavigationConfig config = NavigationConfig.builder() .maxDistance(50.0f) .canSwim(true) .build(); List path = navGraph.findPath( npc.getPosition(), targetPosition, config ); if (path != null && !path.isEmpty()) { npc.getPathFollower().setPath(path); } ``` ## Movement Behaviors Pre-built movement behaviors for common patterns. ### WanderBehavior ```java public class WanderBehavior { private float wanderRadius; private float minPauseDuration; private float maxPauseDuration; public WanderBehavior(float radius) { this.wanderRadius = radius; } public void update(NPCEntity npc, float deltaTime) { if (!npc.getMotionController().isMoving()) { Vector3d wanderTarget = calculateWanderTarget(npc); npc.getMotionController().moveTo(wanderTarget); } } } ``` ### FollowBehavior ```java public class FollowBehavior { private Entity target; private float followDistance; private float catchUpDistance; public void update(NPCEntity npc, float deltaTime) { float distance = npc.getPosition().distance(target.getPosition()); if (distance > catchUpDistance) { npc.getMotionController().run(); } else if (distance > followDistance) { npc.getMotionController().walk(); npc.getMotionController().moveTo(target.getPosition()); } else { npc.getMotionController().stop(); } } } ``` ### PatrolBehavior ```java public class PatrolBehavior { private List patrolPoints; private int currentPointIndex; private boolean loop; private float waitTimeAtPoint; public void update(NPCEntity npc, float deltaTime) { Vector3d currentTarget = patrolPoints.get(currentPointIndex); MotionController motion = npc.getMotionController(); if (motion.hasReachedTarget()) { // Wait at point currentPointIndex = (currentPointIndex + 1) % patrolPoints.size(); } else { motion.moveTo(currentTarget); } } } ``` ### FleeBehavior ```java public class FleeBehavior { private float fleeDistance; private Entity threat; public void update(NPCEntity npc, float deltaTime) { Vector3d awayFromThreat = npc.getPosition() .subtract(threat.getPosition()) .normalize() .multiply(fleeDistance); Vector3d fleeTarget = npc.getPosition().add(awayFromThreat); npc.getMotionController().sprint(); npc.getMotionController().moveTo(fleeTarget); } } ``` ## Obstacle Avoidance ```java public class ObstacleAvoidance { private float avoidanceRadius; private float lookAheadDistance; public Vector3d calculateAvoidanceVector( NPCEntity npc, Vector3d desiredDirection ) { // Cast rays to detect obstacles List obstacles = castAvoidanceRays(npc, desiredDirection); if (obstacles.isEmpty()) { return desiredDirection; } // Calculate avoidance steering Vector3d avoidance = Vector3d.ZERO; for (RaycastHit hit : obstacles) { Vector3d away = npc.getPosition().subtract(hit.position).normalize(); avoidance = avoidance.add(away); } return desiredDirection.add(avoidance.normalize()).normalize(); } } ``` ## Movement Systems ECS systems that process NPC movement: ```java // Movement update system public class NPCMovementSystem implements System { @Override public void update(float deltaTime) { for (NPCEntity npc : npcsWithMovement) { MotionController motion = npc.getMotionController(); PathFollower path = npc.getPathFollower(); // Update path following if (path.hasPath()) { updatePathFollowing(npc, motion, path, deltaTime); } // Apply movement motion.update(deltaTime); } } } ``` ## Best Practices {{< callout type="info" >}} **Movement Guidelines:** - Use NavigationConfig appropriate for the NPC type - Cache paths when possible to avoid frequent recalculation - Use path smoothing for more natural movement - Consider entity size when pathfinding - Implement obstacle avoidance for dynamic environments {{< /callout >}} {{< callout type="warning" >}} **Pathfinding Performance:** - Limit pathfinding requests per frame - Use shorter max distances when possible - Cache frequently used paths - Consider hierarchical pathfinding for large worlds {{< /callout >}}