From 92500b5ba668bc5312a38a33cf31223996c8c7e0 Mon Sep 17 00:00:00 2001 From: Chris Bradfield Date: Sun, 4 Mar 2018 22:13:00 -0800 Subject: [PATCH] Update ray-casting tutorial --- tutorials/physics/ray-casting.rst | 85 +++++++++++++++++++------------ 1 file changed, 53 insertions(+), 32 deletions(-) diff --git a/tutorials/physics/ray-casting.rst b/tutorials/physics/ray-casting.rst index 7bc3c4a9cab..892f0bd23fb 100644 --- a/tutorials/physics/ray-casting.rst +++ b/tutorials/physics/ray-casting.rst @@ -26,9 +26,9 @@ 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 calling -:ref:`CanvasItem.get_world_2d().get_space() `. -For 3D, it's :ref:`Spatial.get_world().get_space() `. +can be obtained by accessing +:ref:`CanvasItem.get_world_2d().space `. +For 3D, it's :ref:`Spatial.get_world().space `. The resulting space :ref:`RID ` can be used in :ref:`PhysicsServer ` and @@ -49,51 +49,52 @@ To perform queries into physics space, the and :ref:`PhysicsDirectSpaceState ` must be used. -In code, for 2D spacestate, this code must be used: +Use the following code in 2D: :: func _physics_process(delta): - var space_rid = get_world_2d().get_space() + var space_rid = get_world_2d().space var space_state = Physics2DServer.space_get_direct_state(space_rid) -Of course, there is a simpler shortcut: +Or more directly: :: func _physics_process(delta): - var space_state = get_world_2d().get_direct_space_state() + var space_state = get_world_2d().direct_space_state -For 3D: +And in 3D: :: func _physics_process(delta): - var space_state = get_world().get_direct_space_state() + var space_state = get_world().direct_space_state Raycast query ------------- For performing a 2D raycast query, the method :ref:`Physics2DDirectSpaceState.intersect_ray() ` -must be used, for example: +may be used. For example: :: func _physics_process(delta): - var space_state = get_world().get_direct_space_state() + var space_state = get_world().direct_space_state # use global coordinates, not local to node var result = space_state.intersect_ray(Vector2(0, 0), Vector2(50, 100)) -Result is a dictionary. If the ray didn't hit anything, the dictionary will +The result is a dictionary. If the ray didn't hit anything, the dictionary will be empty. If it did hit something it will contain collision information: :: - if not result.empty(): + if result: print("Hit at point: ", result.position) -The collision result dictionary, when something hit, has this format: +The ``result`` dictionary when a collision occurs contains the following +data: :: @@ -107,33 +108,53 @@ The collision result dictionary, when something hit, has this format: metadata: Variant() # metadata of collider } - # in case of 3D, Vector3 is returned. +The data is similar in 3D space, using Vector3 coordinates. Collision exceptions -------------------- -It is a very common case to attempt casting a ray from a character or -another game scene to try to infer properties of the world around it. -The problem with this is that the same character has a collider, so the -ray can never leave the origin (it will keep hitting its own collider), -as evidenced in the following image. +A common use case for ray casting is to enable a character to gather data +about the world around it. One problem with this is that the same character +has a collider, so the ray will only detect its parent's collider, +as shown in the following image: .. image:: img/raycast_falsepositive.png -To avoid self-intersection, the intersect_ray() function can take an +To avoid self-intersection, the ``intersect_ray()`` function can take an optional third parameter which is an array of exceptions. This is an example of how to use it from a KinematicBody2D or any other -collisionobject based node: +collision object node: :: extends KinematicBody2D func _physics_process(delta): - var space_state = get_world().get_direct_space_state() - var result = space_state.intersect_ray(get_global_pos(), enemy_pos, [self]) + var space_state = get_world().direct_space_state + var result = space_state.intersect_ray(global_position, enemy_position, [self]) + +The exceptions array can contain objects or RIDs. + +Collision Mask +-------------- + +While the exceptions method works fine for excluding the parent body, it becomes +very inconvenient if you need a large and/or dynamic list of exceptions. In +this case, it is much more efficient to use the collision layer/mask system. + +The optional fourth argument for ``intersect_ray()`` is a collision mask. For +example, to use same mask as the parent body, use the ``collision_mask`` +member variable: + +:: + + extends KinematicBody2D + + func _physics_process(delta): + var space_state = get_world().direct_space_state + var result = space_state.intersect_ray(global_position, enemy_position, + [self], collision_mask) -The extra argument is a list of exceptions, can be objects or RIDs. 3D ray casting from screen -------------------------- @@ -144,11 +165,11 @@ picking. There is not much of a need to do this because has an "input_event" signal that will let you know when it was clicked, but in case there is any desire to do it manually, here's how. -To cast a ray from the screen, the :ref:`Camera ` node -is needed. Camera can be in two projection modes, perspective and +To cast a ray from the screen, you need a :ref:`Camera ` +node. A ``Camera`` can be in two projection modes: perspective and orthogonal. Because of this, both the ray origin and direction must be -obtained. (origin changes in orthogonal, while direction changes in -perspective): +obtained. This is because ``origin`` changes in orthogonal mode, while +``normal`` changes in perspective mode: .. image:: img/raycast_projection.png @@ -160,9 +181,9 @@ To obtain it using a camera, the following code can be used: func _input(event): if event is InputEventMouseButton and event.pressed and event.button_index == 1: - var camera = get_node("camera") + var camera = $Camera var from = camera.project_ray_origin(event.position) var to = from + camera.project_ray_normal(event.position) * ray_length -Of course, remember that during ``_input()``, space may be locked, so save -your query for ``_physics_process()``. +Remember that during ``_input()``, the space may be locked, so in practice +this query should be run in ``_physics_process()``.