9.2 KiB
9.2 KiB
title, type, weight
| title | type | weight |
|---|---|---|
| Mouvement NPC | docs | 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.movementcom.hypixel.hytale.server.npc.navigation
Motion Controller
Le MotionController exécute les commandes de mouvement et gère la locomotion des NPCs.
Classe MotionController
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
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
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
public class PathFollower {
private List<Vector3d> path;
private int currentIndex;
private float waypointRadius;
private boolean smoothPath;
// Gestion de chemin
public void setPath(List<Vector3d> 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
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
public class NavigationGraph {
// Trouver un chemin entre deux points
public List<Vector3d> 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
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<String> 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
NavigationGraph navGraph = world.getNavigationGraph();
NavigationConfig config = NavigationConfig.builder()
.maxDistance(50.0f)
.canSwim(true)
.build();
List<Vector3d> 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
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
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
public class PatrolBehavior {
private List<Vector3d> 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
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
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<RaycastHit> 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 :
// 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 >}}