366 lines
9.2 KiB
Markdown
366 lines
9.2 KiB
Markdown
---
|
|
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<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
|
|
|
|
```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<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
|
|
|
|
```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<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
|
|
|
|
```java
|
|
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
|
|
|
|
```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<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
|
|
|
|
```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<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 :
|
|
|
|
```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 >}}
|