Browse Source

Update ray-casting tutorial

Chris Bradfield 7 years ago
parent
commit
92500b5ba6
1 changed files with 53 additions and 32 deletions
  1. 53 32
      tutorials/physics/ray-casting.rst

+ 53 - 32
tutorials/physics/ray-casting.rst

@@ -26,9 +26,9 @@ Space
 
 
 In the physics world, Godot stores all the low level collision and
 In the physics world, Godot stores all the low level collision and
 physics information in a *space*. The current 2d space (for 2D Physics)
 physics information in a *space*. The current 2d space (for 2D Physics)
-can be obtained by calling
-:ref:`CanvasItem.get_world_2d().get_space() <class_CanvasItem_get_world_2d>`.
-For 3D, it's :ref:`Spatial.get_world().get_space() <class_Spatial_get_world>`.
+can be obtained by accessing
+:ref:`CanvasItem.get_world_2d().space <class_CanvasItem_get_world_2d>`.
+For 3D, it's :ref:`Spatial.get_world().space <class_Spatial_get_world>`.
 
 
 The resulting space :ref:`RID <class_RID>` can be used in
 The resulting space :ref:`RID <class_RID>` can be used in
 :ref:`PhysicsServer <class_PhysicsServer>` and
 :ref:`PhysicsServer <class_PhysicsServer>` and
@@ -49,51 +49,52 @@ To perform queries into physics space, the
 and :ref:`PhysicsDirectSpaceState <class_PhysicsDirectSpaceState>`
 and :ref:`PhysicsDirectSpaceState <class_PhysicsDirectSpaceState>`
 must be used.
 must be used.
 
 
-In code, for 2D spacestate, this code must be used:
+Use the following code in 2D:
 
 
 ::
 ::
 
 
     func _physics_process(delta):
     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)
         var space_state = Physics2DServer.space_get_direct_state(space_rid)
 
 
-Of course, there is a simpler shortcut:
+Or more directly:
 
 
 ::
 ::
 
 
     func _physics_process(delta):
     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):
     func _physics_process(delta):
-        var space_state = get_world().get_direct_space_state()
+        var space_state = get_world().direct_space_state
 
 
 Raycast query
 Raycast query
 -------------
 -------------
 
 
 For performing a 2D raycast query, the method
 For performing a 2D raycast query, the method
 :ref:`Physics2DDirectSpaceState.intersect_ray() <class_Physics2DDirectSpaceState_intersect_ray>`
 :ref:`Physics2DDirectSpaceState.intersect_ray() <class_Physics2DDirectSpaceState_intersect_ray>`
-must be used, for example:
+may be used. For example:
 
 
 ::
 ::
 
 
     func _physics_process(delta):
     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
         # use global coordinates, not local to node
         var result = space_state.intersect_ray(Vector2(0, 0), Vector2(50, 100))
         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:
 be empty. If it did hit something it will contain collision information:
 
 
 ::
 ::
 
 
-        if not result.empty():
+        if result:
             print("Hit at point: ", result.position)
             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
        metadata: Variant() # metadata of collider
     }
     }
 
 
-    # in case of 3D, Vector3 is returned.
+The data is similar in 3D space, using Vector3 coordinates.
 
 
 Collision exceptions
 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
 .. 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
 optional third parameter which is an array of exceptions. This is an
 example of how to use it from a KinematicBody2D or any other
 example of how to use it from a KinematicBody2D or any other
-collisionobject based node:
+collision object node:
 
 
 ::
 ::
 
 
     extends KinematicBody2D
     extends KinematicBody2D
 
 
     func _physics_process(delta):
     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
 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,
 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.
 but in case there is any desire to do it manually, here's how.
 
 
-To cast a ray from the screen, the :ref:`Camera <class_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 <class_Camera>`
+node. A ``Camera`` can be in two projection modes: perspective and
 orthogonal. Because of this, both the ray origin and direction must be
 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
 .. image:: img/raycast_projection.png
 
 
@@ -160,9 +181,9 @@ To obtain it using a camera, the following code can be used:
 
 
     func _input(event):
     func _input(event):
         if event is InputEventMouseButton and event.pressed and event.button_index == 1:
         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 from = camera.project_ray_origin(event.position)
               var to = from + camera.project_ray_normal(event.position) * ray_length
               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()``.