ray-casting.rst 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  1. .. _doc_ray-casting:
  2. Ray-casting
  3. ===========
  4. Introduction
  5. ------------
  6. One of the most common tasks in game development is casting a ray (or
  7. custom shaped object) and checking what it hits. This enables complex
  8. behaviors, AI, etc. to take place. This tutorial will explain how to
  9. do this in 2D and 3D.
  10. Godot stores all the low level game information in servers, while the
  11. scene is only a frontend. As such, ray casting is generally a
  12. lower-level task. For simple raycasts, nodes like
  13. :ref:`RayCast3D <class_RayCast3D>` and :ref:`RayCast2D <class_RayCast2D>`
  14. will work, as they return every frame what the result of a raycast
  15. is.
  16. Many times, though, ray-casting needs to be a more interactive process
  17. so a way to do this by code must exist.
  18. Space
  19. -----
  20. In the physics world, Godot stores all the low level collision and
  21. physics information in a *space*. The current 2d space (for 2D Physics)
  22. can be obtained by accessing
  23. :ref:`CanvasItem.get_world_2d().space <class_CanvasItem_method_get_world_2d>`.
  24. For 3D, it's :ref:`Node3D.get_world_3d().space <class_Node3D_method_get_world_3d>`.
  25. The resulting space :ref:`RID <class_RID>` can be used in
  26. :ref:`PhysicsServer3D <class_PhysicsServer3D>` and
  27. :ref:`PhysicsServer2D <class_PhysicsServer2D>` respectively for 3D and 2D.
  28. Accessing space
  29. ---------------
  30. Godot physics runs by default in the same thread as game logic, but may
  31. be set to run on a separate thread to work more efficiently. Due to
  32. this, the only time accessing space is safe is during the
  33. :ref:`Node._physics_process() <class_Node_private_method__physics_process>`
  34. callback. Accessing it from outside this function may result in an error
  35. due to space being *locked*.
  36. To perform queries into physics space, the
  37. :ref:`PhysicsDirectSpaceState2D <class_PhysicsDirectSpaceState2D>`
  38. and :ref:`PhysicsDirectSpaceState3D <class_PhysicsDirectSpaceState3D>`
  39. must be used.
  40. Use the following code in 2D:
  41. .. tabs::
  42. .. code-tab:: gdscript GDScript
  43. func _physics_process(delta):
  44. var space_rid = get_world_2d().space
  45. var space_state = PhysicsServer2D.space_get_direct_state(space_rid)
  46. .. code-tab:: csharp
  47. public override void _PhysicsProcess(double delta)
  48. {
  49. var spaceRid = GetWorld2D().Space;
  50. var spaceState = Physics2DServer.SpaceGetDirectState(spaceRid);
  51. }
  52. Or more directly:
  53. .. tabs::
  54. .. code-tab:: gdscript GDScript
  55. func _physics_process(delta):
  56. var space_state = get_world_2d().direct_space_state
  57. .. code-tab:: csharp
  58. public override void _PhysicsProcess(double delta)
  59. {
  60. var spaceState = GetWorld2D().DirectSpaceState;
  61. }
  62. And in 3D:
  63. .. tabs::
  64. .. code-tab:: gdscript GDScript
  65. func _physics_process(delta):
  66. var space_state = get_world_3d().direct_space_state
  67. .. code-tab:: csharp
  68. public override void _PhysicsProcess(double delta)
  69. {
  70. var spaceState = GetWorld3D().DirectSpaceState;
  71. }
  72. Raycast query
  73. -------------
  74. For performing a 2D raycast query, the method
  75. :ref:`PhysicsDirectSpaceState2D.intersect_ray() <class_PhysicsDirectSpaceState2D_method_intersect_ray>`
  76. may be used. For example:
  77. .. tabs::
  78. .. code-tab:: gdscript GDScript
  79. func _physics_process(delta):
  80. var space_state = get_world_2d().direct_space_state
  81. # use global coordinates, not local to node
  82. var query = PhysicsRayQueryParameters2D.create(Vector2(0, 0), Vector2(50, 100))
  83. var result = space_state.intersect_ray(query)
  84. .. code-tab:: csharp
  85. public override void _PhysicsProcess(double delta)
  86. {
  87. var spaceState = GetWorld2D().DirectSpaceState;
  88. // use global coordinates, not local to node
  89. var query = PhysicsRayQueryParameters2D.Create(Vector2.Zero, new Vector2(50, 100));
  90. var result = spaceState.IntersectRay(query);
  91. }
  92. The result is a dictionary. If the ray didn't hit anything, the dictionary will
  93. be empty. If it did hit something, it will contain collision information:
  94. .. tabs::
  95. .. code-tab:: gdscript GDScript
  96. if result:
  97. print("Hit at point: ", result.position)
  98. .. code-tab:: csharp
  99. if (result.Count > 0)
  100. GD.Print("Hit at point: ", result["position"]);
  101. The ``result`` dictionary when a collision occurs contains the following
  102. data:
  103. ::
  104. {
  105. position: Vector2 # point in world space for collision
  106. normal: Vector2 # normal in world space for collision
  107. collider: Object # Object collided or null (if unassociated)
  108. collider_id: ObjectID # Object it collided against
  109. rid: RID # RID it collided against
  110. shape: int # shape index of collider
  111. metadata: Variant() # metadata of collider
  112. }
  113. The data is similar in 3D space, using Vector3 coordinates. Note that to enable collisions
  114. with Area3D, the boolean parameter ``collide_with_areas`` must be set to ``true``.
  115. .. tabs::
  116. .. code-tab:: gdscript GDScript
  117. const RAY_LENGTH = 1000
  118. func _physics_process(delta):
  119. var space_state = get_world_3d().direct_space_state
  120. var cam = $Camera3D
  121. var mousepos = get_viewport().get_mouse_position()
  122. var origin = cam.project_ray_origin(mousepos)
  123. var end = origin + cam.project_ray_normal(mousepos) * RAY_LENGTH
  124. var query = PhysicsRayQueryParameters3D.create(origin, end)
  125. query.collide_with_areas = true
  126. var result = space_state.intersect_ray(query)
  127. Collision exceptions
  128. --------------------
  129. A common use case for ray casting is to enable a character to gather data
  130. about the world around it. One problem with this is that the same character
  131. has a collider, so the ray will only detect its parent's collider,
  132. as shown in the following image:
  133. .. image:: img/raycast_falsepositive.webp
  134. To avoid self-intersection, the ``intersect_ray()`` parameters object can take an
  135. array of exceptions via its ``exclude`` property. This is an example of how to use it
  136. from a CharacterBody2D or any other collision object node:
  137. .. tabs::
  138. .. code-tab:: gdscript GDScript
  139. extends CharacterBody2D
  140. func _physics_process(delta):
  141. var space_state = get_world_2d().direct_space_state
  142. var query = PhysicsRayQueryParameters2D.create(global_position, player_position)
  143. query.exclude = [self]
  144. var result = space_state.intersect_ray(query)
  145. .. code-tab:: csharp
  146. using Godot;
  147. public partial class MyCharacterBody2D : CharacterBody2D
  148. {
  149. public override void _PhysicsProcess(double delta)
  150. {
  151. var spaceState = GetWorld2D().DirectSpaceState;
  152. var query = PhysicsRayQueryParameters2D.Create(globalPosition, playerPosition);
  153. query.Exclude = new Godot.Collections.Array<Rid> { GetRid() };
  154. var result = spaceState.IntersectRay(query);
  155. }
  156. }
  157. The exceptions array can contain objects or RIDs.
  158. Collision Mask
  159. --------------
  160. While the exceptions method works fine for excluding the parent body, it becomes
  161. very inconvenient if you need a large and/or dynamic list of exceptions. In
  162. this case, it is much more efficient to use the collision layer/mask system.
  163. The ``intersect_ray()`` parameters object can also be supplied a collision mask.
  164. For example, to use the same mask as the parent body, use the ``collision_mask``
  165. member variable. The array of exceptions can be supplied as the last argument as well:
  166. .. tabs::
  167. .. code-tab:: gdscript GDScript
  168. extends CharacterBody2D
  169. func _physics_process(delta):
  170. var space_state = get_world_2d().direct_space_state
  171. var query = PhysicsRayQueryParameters2D.create(global_position, target_position,
  172. collision_mask, [self])
  173. var result = space_state.intersect_ray(query)
  174. .. code-tab:: csharp
  175. using Godot;
  176. public partial class MyCharacterBody2D : CharacterBody2D
  177. {
  178. public override void _PhysicsProcess(double delta)
  179. {
  180. var spaceState = GetWorld2D().DirectSpaceState;
  181. var query = PhysicsRayQueryParameters2D.Create(globalPosition, targetPosition,
  182. CollisionMask, new Godot.Collections.Array<Rid> { GetRid() });
  183. var result = spaceState.IntersectRay(query);
  184. }
  185. }
  186. See :ref:`doc_physics_introduction_collision_layer_code_example` for details on how to set the collision mask.
  187. 3D ray casting from screen
  188. --------------------------
  189. Casting a ray from screen to 3D physics space is useful for object
  190. picking. There is not much need to do this because
  191. :ref:`CollisionObject3D <class_CollisionObject3D>`
  192. has an "input_event" signal that will let you know when it was clicked,
  193. but in case there is any desire to do it manually, here's how.
  194. To cast a ray from the screen, you need a :ref:`Camera3D <class_Camera3D>`
  195. node. A ``Camera3D`` can be in two projection modes: perspective and
  196. orthogonal. Because of this, both the ray origin and direction must be
  197. obtained. This is because ``origin`` changes in orthogonal mode, while
  198. ``normal`` changes in perspective mode:
  199. .. image:: img/raycast_projection.png
  200. To obtain it using a camera, the following code can be used:
  201. .. tabs::
  202. .. code-tab:: gdscript GDScript
  203. const RAY_LENGTH = 1000.0
  204. func _input(event):
  205. if event is InputEventMouseButton and event.pressed and event.button_index == 1:
  206. var camera3d = $Camera3D
  207. var from = camera3d.project_ray_origin(event.position)
  208. var to = from + camera3d.project_ray_normal(event.position) * RAY_LENGTH
  209. .. code-tab:: csharp
  210. private const float RayLength = 1000.0f;
  211. public override void _Input(InputEvent @event)
  212. {
  213. if (@event is InputEventMouseButton eventMouseButton && eventMouseButton.Pressed && eventMouseButton.ButtonIndex == MouseButton.Left)
  214. {
  215. var camera3D = GetNode<Camera3D>("Camera3D");
  216. var from = camera3D.ProjectRayOrigin(eventMouseButton.Position);
  217. var to = from + camera3D.ProjectRayNormal(eventMouseButton.Position) * RayLength;
  218. }
  219. }
  220. Remember that during ``_input()``, the space may be locked, so in practice
  221. this query should be run in ``_physics_process()``.