ray-casting.rst 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  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 see 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 just a frontend. As such, ray casting is generally a
  12. lower-level task. For simple raycasts, node such as
  13. :ref:`RayCast <class_RayCast>` and :ref:`RayCast2D <class_RayCast2D>`
  14. will work, as they will 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 calling
  23. :ref:`CanvasItem.get_world_2d().get_space() <class_CanvasItem_get_world_2d>`.
  24. For 3D, it's :ref:`Spatial.get_world().get_space() <class_Spatial_get_world>`.
  25. The resulting space :ref:`RID <class_RID>` can be used in
  26. :ref:`PhysicsServer <class_PhysicsServer>` and
  27. :ref:`Physics2DServer <class_Physics2DServer>` 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._fixed_process() <class_Node__fixed_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:`Physics2DDirectSpaceState <class_Physics2DDirectSpaceState>`
  38. and :ref:`PhysicsDirectSpaceState <class_PhysicsDirectSpaceState>`
  39. must be used.
  40. In code, for 2D spacestate, this code must be used:
  41. ::
  42. func _fixed_process(delta):
  43. var space_rid = get_world_2d().get_space()
  44. var space_state = Physics2DServer.space_get_direct_state(space_rid)
  45. Of course, there is a simpler shortcut:
  46. ::
  47. func _fixed_process(delta):
  48. var space_state = get_world_2d().get_direct_space_state()
  49. For 3D:
  50. ::
  51. func _fixed_process(delta):
  52. var space_state = get_world().get_direct_space_state()
  53. Raycast query
  54. -------------
  55. For performing a 2D raycast query, the method
  56. :ref:`Physics2DDirectSpaceState.intersect_ray() <class_Physics2DDirectSpaceState_intersect_ray>`
  57. must be used, for example:
  58. ::
  59. func _fixed_process(delta):
  60. var space_state = get_world().get_direct_space_state()
  61. # use global coordinates, not local to node
  62. var result = space_state.intersect_ray( Vector2(0,0), Vector2(50,100) )
  63. Result is a dictionary, if ray didn't hit anything, the dictionary will
  64. be empty. If it did hit something it will contain collision information:
  65. ::
  66. if (not result.empty()):
  67. print("Hit at point: ",result.position)
  68. The collision result dictionary, when something hit, has this format:
  69. ::
  70. {
  71. position:Vector2 # point in world space for collision
  72. normal:Vector2 # normal in world space for collision
  73. collider:Object # Object collided or null (if unassociated)
  74. collider_id:ObjectID # Object it collided against
  75. rid:RID # RID it collided against
  76. shape:int # shape index of collider
  77. metadata:Variant() # metadata of collider
  78. }
  79. # in case of 3D, Vector3 is returned.
  80. Collision exceptions
  81. --------------------
  82. It is a very common case to attempt casting a ray from a character or
  83. another game scene to try to infer properties of the world around it.
  84. The problem with this is that the same character has a collider, so the
  85. ray can never leave the origin (it will keep hitting it's own collider),
  86. as evidenced in the following image.
  87. .. image:: /img/raycast_falsepositive.png
  88. To avoid self-intersection, the intersect_ray() function can take an
  89. optional third parameter which is an array of exceptions. This is an
  90. example of how to use it from a KinematicBody2D or any other
  91. collisionobject based node:
  92. ::
  93. extends KinematicBody2D
  94. func _fixed_process(delta):
  95. var space_state = get_world().get_direct_space_state()
  96. var result = space_state.intersect_ray( get_global_pos(), enemy_pos, [ self ] )
  97. The extra argument is a list of exceptions, can be objects or RIDs.
  98. 3D ray casting from screen
  99. --------------------------
  100. Casting a ray from screen to 3D physics space is useful for object
  101. picking. There is not much of a need to do this because
  102. :ref:`CollisionObject <class_CollisionObject>`
  103. has an "input_event" signal that will let you know when it was clicked,
  104. but in case there is any desire to do it manually, here's how.
  105. To cast a ray from the screen, the :ref:`Camera <class_Camera>` node
  106. is needed. Camera can be in two projection modes, perspective and
  107. orthogonal. Because of this, both the ray origin and direction must be
  108. obtained. (origin changes in orthogonal, while direction changes in
  109. perspective):
  110. .. image:: /img/raycast_projection.png
  111. To obtain it using a camera, the following code can be used:
  112. ::
  113. const ray_length = 1000
  114. func _input(ev):
  115. if ev.type==InputEvent.MOUSE_BUTTON and ev.pressed and ev.button_index==1:
  116. var camera = get_node("camera")
  117. var from = camera.project_ray_origin(ev.pos)
  118. var to = from + camera.project_ray_normal(ev.pos) * ray_length
  119. Of course, remember that during ``_input()``, space may be locked, so save
  120. your query for ``_fixed_process()``.