Raycasting de la physique dans le développement de jeux

introduction

L'une des tâches les plus courantes dans le développement de jeux consiste à lancer des rayons (ou des objets de forme personnalisée) et à vérifier leur impact. Cela permet des comportements complexes, l'IA, etc. Ce tutoriel vous expliquera comment faire cela en 2D et 3D.

Godot stocke toutes les informations de jeu de bas niveau sur le serveur, et la scène n'est que le front-end. Par conséquent, la projection de rayons est généralement une tâche de niveau inférieur. Pour une diffusion de rayon simple, des nœuds tels que RayCast et RayCast2D fonctionneront car ils renverront le résultat de la diffusion de rayon dans chaque image.

Cependant, dans de nombreux cas, la diffusion de rayons doit être un processus plus interactif, il doit donc y avoir un moyen d'effectuer une diffusion de rayons à travers le code.

espace

Dans le monde physique, Godot stocke toutes les collisions de bas niveau et les informations physiques dans un seul espace. Il est accessible via CanvasItem.get_world_2d (). space Récupère l'espace 2D actuel (pour la physique 2D). Pour la 3D, il s'agit de Spatial.get_world (). espace.

Le RID spatial généré peut être utilisé pour la 3D et la 2D dans PhysicsServer et Physics2DServer respectivement.

Entrez dans l'espace

Godot Physics fonctionne dans le même thread que la logique du jeu par défaut, mais peut être configuré pour s'exécuter sur un thread séparé pour fonctionner plus efficacement. Par conséquent, le seul moment sûr pour accéder à l'espace est pendant le rappel Node._physics_process (). Puisque l'espace est verrouillé, y accéder depuis l'extérieur de cette fonction peut provoquer des erreurs.

Pour effectuer des requêtes sur l'espace physique, Physics2DDirectSpaceState et PhysicsDirectSpaceState doivent être utilisés.

Utilisez le code suivant en 2D:

public override void _PhysicsProcess(float delta)
{
    
    
    var spaceRid = GetWorld2d().Space;
    var spaceState = Physics2DServer.SpaceGetDirectState(spaceRid);
}

Ou plus directement:

public override void _PhysicsProcess(float delta)
{
    
    
    var spaceState = GetWorld2d().DirectSpaceState;
}

En 3D:

public override void _PhysicsProcess(float delta)
{
    
    
    var spaceState = GetWorld().DirectSpaceState;
}

Requête Raycast

Afin d'effectuer une requête de diffusion de rayons 2D, la méthode Physics2DDirectSpaceState.intersect_ray () peut être utilisée. Par exemple:

public override void _PhysicsProcess(float delta)
{
    
    
    var spaceState = GetWorld2d().DirectSpaceState;
    // use global coordinates, not local to node
    var result = spaceState.IntersectRay(new Vector2(), new Vector2(50, 100));
}

Le résultat est un dictionnaire. Si le rayon ne touche rien, le dictionnaire sera vide. S'il touche quelque chose, il contiendra des informations de collision:

if (result.Count > 0)
    GD.Print("Hit at point: ", result["position"]);

Le dictionnaire au moment de la collision des résultats contient les données suivantes:

{
    
    
   position: Vector2 # point in world space for collision
   normal: Vector2 # normal in world space for collision
   collider: Object # Object collided or null (if unassociated)
   collider_id: ObjectID # Object it collided against
   rid: RID # RID it collided against
   shape: int # shape index of collider
   metadata: Variant() # metadata of collider
}

En utilisant les coordonnées Vector3, les données sont similaires dans l'espace 3D.

Exception de collision

Un cas d'utilisation courant du lancer de rayons est de permettre à un personnage de collecter des données sur le monde qui l'entoure. Un problème avec ceci est que le même personnage a un collisionneur, donc la lumière ne détectera que le collisionneur de son parent, comme indiqué ci-dessous:

../../_images/raycast_falsepositive.png

Pour éviter l'auto-intersection, la intersect_ray()fonction peut prendre un troisième paramètre facultatif, qui est un ensemble d'exceptions. Voici un exemple de son utilisation à partir de KinematicBody2D ou de tout autre nœud d'objet de collision:

class Body : KinematicBody2D
{
    
    
    public override void _PhysicsProcess(float delta)
    {
    
    
        var spaceState = GetWorld2d().DirectSpaceState;
        var result = spaceState.IntersectRay(globalPosition, enemyPosition, new object[] {
    
     this });
    }
}

Le tableau d'exceptions peut contenir des objets ou des RID.

Masque anti-collision

Bien que la méthode d'exception puisse très bien exclure le corps parent, elle deviendra très gênante si vous avez besoin d'une liste d'exceptions volumineuse et / ou dynamique. Dans ce cas, il est plus efficace d'utiliser un système de couche / masque de collision.

Le quatrième paramètre facultatif intersect_ray () est le masque de collision. Par exemple, pour utiliser le même masque que l'entité parente, utilisez la variable membre collision_mask:

class Body : KinematicBody2D
{
    
    
    public override void _PhysicsProcess(float delta)
    {
    
    
        var spaceState = GetWorld2d().DirectSpaceState;
        var result = spaceState.IntersectRay(globalPosition, enemyPosition,
                        new object[] {
    
     this }, CollisionMask);
    }
}

Pour plus d'informations sur la définition du masque de collision, consultez l' exemple de code .

Projection de rayons 3D sur l'écran

La projection de la lumière de l'écran dans l'espace physique 3D est utile pour ramasser des objets. Inutile de le faire, car CollisionObject a un signal "input_event" qui vous permet de savoir quand cliquer dessus, mais si vous souhaitez le faire manuellement, veuillez suivre les étapes ci-dessous.

Pour projeter la lumière depuis l'écran, vous avez besoin d'un nœud Caméra. ACamera peut utiliser deux modes de projection: perspective et orthogonal. Par conséquent, le point de départ et la direction du rayon doivent être obtenus en même temps. En effet, l'origine changera normalement en mode orthogonal, mais changera en mode perspective:

../../_images/raycast_projection.png

Pour l'obtenir à l'aide de l'appareil photo, vous pouvez utiliser le code suivant:

private const float rayLength = 1000;

public override void _Input(InputEvent @event)
{
    
    
    if (@event is InputEventMouseButton eventMouseButton && eventMouseButton.Pressed && eventMouseButton.ButtonIndex == 1)
    {
    
    
        var camera = (Camera)GetNode("Camera");
        var from = camera.ProjectRayOrigin(eventMouseButton.Position);
        var to = from + camera.ProjectRayNormal(eventMouseButton.Position) * rayLength;
    }
}

Gardez à l'esprit que pendant la période _input(), l'espace peut être verrouillé, donc en fait la requête doit être exécutée _physics_process().

Je suppose que tu aimes

Origine blog.csdn.net/qq_44273429/article/details/111592686
conseillé
Classement