Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
Raycasting
Einführung
Eine der häufigsten Aufgaben in der Spieleentwicklung besteht darin, einen (Licht-) Strahl (oder ein Objekt mit benutzerdefinierter Geometrie) zu werfen und zu überprüfen, auf was er trifft. Dies ermöglicht komplexe Verhaltensweisen, KI usw. In diesem Tutorial wird erklärt, wie dies in 2D und 3D funktioniert.
Godot stores all the low-level game information in servers, while the scene is only a frontend. As such, ray casting is generally a lower-level task. For simple raycasts, nodes like RayCast3D and RayCast2D will work, as they return every frame what the result of a raycast is.
In vielen Fällen muss Raycasting jedoch ein interaktiverer Prozess sein, sodass eine Möglichkeit vorhanden sein muss, dies per Code zu tun.
Space
In the physics world, Godot stores all the low-level collision and physics information in a space. The current 2d space (for 2D Physics) can be obtained by accessing CanvasItem.get_world_2d().space. For 3D, it's Node3D.get_world_3d().space.
Der resultierende Space RID kann in PhysicsServer3D und PhysicsServer2D jeweils für 3D und 2D verwendet werden.
Zugriff auf den Space
Die Godot-Physik läuft standardmäßig im selben Thread wie die Spiellogik, kann aber zur effizienteren Arbeit in einem separaten Thread ausgeführt werden. Aus diesem Grund ist der einzige Zeitpunkt, zu dem der Zugriff auf den Space sicher ist, während des Node._physics_process()-Callbacks. Der Zugriff von außerhalb dieser Funktion kann zu einem Fehler führen, da der Space gesperrt ist.
Um Abfragen im Physik-Space durchzuführen, müssen PhysicsDirectSpaceState2D und PhysicsDirectSpaceState3D verwendet werden.
Verwenden Sie den folgenden Code in 2D:
func _physics_process(delta):
var space_rid = get_world_2d().space
var space_state = PhysicsServer2D.space_get_direct_state(space_rid)
public override void _PhysicsProcess(double delta)
{
var spaceRid = GetWorld2D().Space;
var spaceState = PhysicsServer2D.SpaceGetDirectState(spaceRid);
}
Oder direkter:
func _physics_process(delta):
var space_state = get_world_2d().direct_space_state
public override void _PhysicsProcess(double delta)
{
var spaceState = GetWorld2D().DirectSpaceState;
}
Und in 3D:
func _physics_process(delta):
var space_state = get_world_3d().direct_space_state
public override void _PhysicsProcess(double delta)
{
var spaceState = GetWorld3D().DirectSpaceState;
}
Raycast-Abfrage
Zur Durchführung einer 2D-Raycast-Abfrage kann die Methode PhysicsDirectSpaceState2D.intersect_ray() verwendet werden. Ein Beispiel:
func _physics_process(delta):
var space_state = get_world_2d().direct_space_state
# use global coordinates, not local to node
var query = PhysicsRayQueryParameters2D.create(Vector2(0, 0), Vector2(50, 100))
var result = space_state.intersect_ray(query)
public override void _PhysicsProcess(double delta)
{
var spaceState = GetWorld2D().DirectSpaceState;
// use global coordinates, not local to node
var query = PhysicsRayQueryParameters2D.Create(Vector2.Zero, new Vector2(50, 100));
var result = spaceState.IntersectRay(query);
}
Das Ergebnis ist ein Dictionary. Wenn der Strahl auf nichts trifft, wird das Dictionary leer sein. Wenn er etwas trifft, wird das Dictionary die Kollisionsinformationen enthalten:
if result:
print("Hit at point: ", result.position)
if (result.Count > 0)
GD.Print("Hit at point: ", result["position"]);
Das Ergebnis
-Dictionary enthält bei einer Kollision die folgenden Daten:
{
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
}
Die Daten sind im 3D-Raum ähnlich und verwenden Vector3-Koordinaten. Um Kollisionen mit Area3D zu ermöglichen, muss der Bool-Parameter collide_with_areas
auf true
gesetzt werden.
const RAY_LENGTH = 1000
func _physics_process(delta):
var space_state = get_world_3d().direct_space_state
var cam = $Camera3D
var mousepos = get_viewport().get_mouse_position()
var origin = cam.project_ray_origin(mousepos)
var end = origin + cam.project_ray_normal(mousepos) * RAY_LENGTH
var query = PhysicsRayQueryParameters3D.create(origin, end)
query.collide_with_areas = true
var result = space_state.intersect_ray(query)
private const int RayLength = 1000;
public override void _PhysicsProcess(double delta)
{
var spaceState = GetWorld3D().DirectSpaceState;
var cam = GetNode<Camera3D>("Camera3D");
var mousePos = GetViewport().GetMousePosition();
var origin = cam.ProjectRayOrigin(mousePos);
var end = origin + cam.ProjectRayNormal(mousePos) * RayLength;
var query = PhysicsRayQueryParameters3D.Create(origin, end);
query.CollideWithAreas = true;
var result = spaceState.IntersectRay(query);
}
Kollisionsausnahmen
Ein häufiger Anwendungsfall für das Raycasting besteht darin, einem Charakter das Sammeln von Daten über die Welt um ihn herum zu ermöglichen. Ein Problem dabei ist, dass derselbe Charakter einen Collider hat, sodass der Strahl nur den Collider seines Parents erkennt, wie in der folgenden Abbildung gezeigt:

Um Selbstüberschneidungen zu vermeiden, kann das intersect_ray()
-Parameterobjekt über seine exclude
-Property ein Array von Ausnahmen annehmen. Dies ist ein Beispiel, wie man es von einem CharacterBody2D oder einem anderen Kollisionsobjekt-Node aus benutzen kann:
extends CharacterBody2D
func _physics_process(delta):
var space_state = get_world_2d().direct_space_state
var query = PhysicsRayQueryParameters2D.create(global_position, player_position)
query.exclude = [self]
var result = space_state.intersect_ray(query)
using Godot;
public partial class MyCharacterBody2D : CharacterBody2D
{
public override void _PhysicsProcess(double delta)
{
var spaceState = GetWorld2D().DirectSpaceState;
var query = PhysicsRayQueryParameters2D.Create(globalPosition, playerPosition);
query.Exclude = [GetRid()];
var result = spaceState.IntersectRay(query);
}
}
Das Ausnahmen-Array kann Objekte oder RIDs enthalten.
Kollisionsmaske
Während die Ausnahmemethode zum Ausschließen des Parent-Bodys gut funktioniert, ist es sehr unpraktisch, wenn Sie eine große oder dynamische Liste von Ausnahmen benötigen. In diesem Fall ist es viel effizienter, das Kollisionsebenen- bzw. Maskensystem zu verwenden.
Das intersect_ray()
-Parameterobjekt kann auch mit einer Kollisionsmaske versehen werden. Um zum Beispiel die gleiche Maske wie der übergeordnete Body zu verwenden, benutzen Sie die collision_mask
-Membervariable. Das Ausnahmenarray kann auch als letztes Argument übergeben werden:
extends CharacterBody2D
func _physics_process(delta):
var space_state = get_world_2d().direct_space_state
var query = PhysicsRayQueryParameters2D.create(global_position, target_position,
collision_mask, [self])
var result = space_state.intersect_ray(query)
using Godot;
public partial class MyCharacterBody2D : CharacterBody2D
{
public override void _PhysicsProcess(double delta)
{
var spaceState = GetWorld2D().DirectSpaceState;
var query = PhysicsRayQueryParameters2D.Create(globalPosition, targetPosition,
CollisionMask, [GetRid()]);
var result = spaceState.IntersectRay(query);
}
}
Siehe Code-Beispiel für Details zum Festlegen der Kollisionsmaske.
3D-Raycasting vom Bildschirm
Das Werfen eines Strahls vom Bildschirm in den 3D-Physik-Space ist nützlich für die Auswahl von Objekten. Es gibt keinen großen Bedarf, dies zu tun, weil CollisionObject3D ein "input_event"-Signal hat, das Sie wissen lässt, wenn es angeklickt wurde, aber für den Fall, dass Sie es manuell tun möchten, finden Sie hier eine Beschreibung.
Um einen Strahl vom Bildschirm aus zu werfen, braucht man einen Camera3D-Node. Eine Camera3D
kann sich in zwei Projektionsmodi befinden: Perspektive und Orthogonal. Aus diesem Grund müssen sowohl der Ursprung als auch die Richtung des Strahls ermittelt werden. Das liegt daran, dass sich origin
im orthogonalen Modus ändert, während sich normal
im perspektivischen Modus ändert:

Um sie mit einer Kamera zu erhalten, kann der folgende Code verwendet werden:
const RAY_LENGTH = 1000.0
func _input(event):
if event is InputEventMouseButton and event.pressed and event.button_index == 1:
var camera3d = $Camera3D
var from = camera3d.project_ray_origin(event.position)
var to = from + camera3d.project_ray_normal(event.position) * RAY_LENGTH
private const float RayLength = 1000.0f;
public override void _Input(InputEvent @event)
{
if (@event is InputEventMouseButton eventMouseButton && eventMouseButton.Pressed && eventMouseButton.ButtonIndex == MouseButton.Left)
{
var camera3D = GetNode<Camera3D>("Camera3D");
var from = camera3D.ProjectRayOrigin(eventMouseButton.Position);
var to = from + camera3D.ProjectRayNormal(eventMouseButton.Position) * RayLength;
}
}
Denken Sie daran, dass während _input()
der Space gesperrt sein kann, so dass in der Praxis diese Abfrage in _physics_process()
ausgeführt werden sollte.