--- title: Mouvement NPC type: docs weight: 4 --- Le système de mouvement NPC gère la navigation, le pathfinding et le contrôle de mouvement pour les NPCs. **Packages:** - `com.hypixel.hytale.server.npc.movement` - `com.hypixel.hytale.server.npc.navigation` ## Motion Controller Le `MotionController` exécute les commandes de mouvement et gère la locomotion des NPCs. ### Classe MotionController ```java public class MotionController { private NPCEntity npc; private MovementState state; private float currentSpeed; private Vector3d targetVelocity; // Commandes de mouvement public void moveTo(Vector3d target); public void moveInDirection(Vector3d direction); public void stop(); // Contrôle de vitesse public void setSpeed(float speed); public void walk(); public void run(); public void sprint(); // Requêtes d'état public boolean isMoving(); public boolean hasReachedTarget(); public MovementState getState(); } ``` ### États de Mouvement ```java public enum MovementState { IDLE, // Immobile WALKING, // Mouvement normal RUNNING, // Mouvement rapide SPRINTING, // Vitesse maximum JUMPING, // En l'air (saut) FALLING, // En l'air (chute) SWIMMING, // Dans l'eau CLIMBING, // Sur échelle/liane SLIDING // Sur pente } ``` ### Utiliser MotionController ```java NPCEntity npc = // obtenir le NPC MotionController motion = npc.getMotionController(); // Se déplacer vers une position motion.moveTo(targetPosition); // Définir la vitesse de mouvement motion.run(); // ou motion.setSpeed(5.0f); // Vérifier si arrivé if (motion.hasReachedTarget()) { // Destination atteinte } // Arrêter le mouvement motion.stop(); ``` ## Path Follower Le `PathFollower` suit et parcourt les chemins calculés. ### Classe PathFollower ```java public class PathFollower { private List path; private int currentIndex; private float waypointRadius; private boolean smoothPath; // Gestion de chemin public void setPath(List path); public void clearPath(); public boolean hasPath(); // Suivi public Vector3d getNextWaypoint(); public void advanceToNextWaypoint(); public boolean hasReachedWaypoint(Vector3d position); // Progression public float getPathProgress(); // 0.0 à 1.0 public int getRemainingWaypoints(); } ``` ### Exemple de Suivi de Chemin ```java PathFollower pathFollower = npc.getPathFollower(); MotionController motion = npc.getMotionController(); // Définir un chemin pathFollower.setPath(calculatedPath); // Dans la boucle de mise à jour if (pathFollower.hasPath()) { Vector3d nextWaypoint = pathFollower.getNextWaypoint(); // Se déplacer vers le waypoint motion.moveTo(nextWaypoint); // Vérifier si atteint if (pathFollower.hasReachedWaypoint(npc.getPosition())) { pathFollower.advanceToNextWaypoint(); } } ``` ## Navigation Graph Le système de navigation utilise le pathfinding A* sur un graphe de navigation. ### Classe NavigationGraph ```java public class NavigationGraph { // Trouver un chemin entre deux points public List findPath( Vector3d start, Vector3d end, NavigationConfig config ); // Vérifier si un point est navigable public boolean isNavigable(Vector3d position); // Obtenir le point navigable le plus proche public Vector3d getNearestNavigablePoint(Vector3d position); } ``` ### NavigationConfig ```java public class NavigationConfig { private float maxDistance; // Longueur max du chemin private float stepHeight; // Hauteur max de marche private float entityWidth; // Largeur de collision entité private float entityHeight; // Hauteur de collision entité private boolean canSwim; // Autoriser chemins aquatiques private boolean canClimb; // Autoriser échelles/lianes private boolean canOpenDoors; // Autoriser traversée de portes private Set avoidBlocks; // Blocs à éviter // Pattern builder public static NavigationConfig builder() .maxDistance(100.0f) .stepHeight(1.0f) .entityWidth(0.6f) .entityHeight(1.8f) .canSwim(false) .build(); } ``` ### Exemple de Pathfinding ```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); } ``` ## Comportements de Mouvement Comportements de mouvement pré-construits pour les patterns courants. ### 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()) { // Attendre au 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); } } ``` ## Évitement d'Obstacles ```java public class ObstacleAvoidance { private float avoidanceRadius; private float lookAheadDistance; public Vector3d calculateAvoidanceVector( NPCEntity npc, Vector3d desiredDirection ) { // Lancer des rayons pour détecter les obstacles List obstacles = castAvoidanceRays(npc, desiredDirection); if (obstacles.isEmpty()) { return desiredDirection; } // Calculer la direction d'évitement 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(); } } ``` ## Systèmes de Mouvement Systèmes ECS qui traitent le mouvement des NPCs : ```java // Système de mise à jour de mouvement public class NPCMovementSystem implements System { @Override public void update(float deltaTime) { for (NPCEntity npc : npcsWithMovement) { MotionController motion = npc.getMotionController(); PathFollower path = npc.getPathFollower(); // Mettre à jour le suivi de chemin if (path.hasPath()) { updatePathFollowing(npc, motion, path, deltaTime); } // Appliquer le mouvement motion.update(deltaTime); } } } ``` ## Bonnes Pratiques {{< callout type="info" >}} **Directives de Mouvement :** - Utilisez un NavigationConfig approprié au type de NPC - Mettez en cache les chemins quand possible pour éviter les recalculs fréquents - Utilisez le lissage de chemin pour un mouvement plus naturel - Considérez la taille de l'entité lors du pathfinding - Implémentez l'évitement d'obstacles pour les environnements dynamiques {{< /callout >}} {{< callout type="warning" >}} **Performance du Pathfinding :** - Limitez les requêtes de pathfinding par frame - Utilisez des distances max plus courtes quand possible - Mettez en cache les chemins fréquemment utilisés - Considérez le pathfinding hiérarchique pour les grands mondes {{< /callout >}}