kinematic_character_2d.rst 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. .. _doc_kinematic_character_2d:
  2. Kinematic character (2D)
  3. ========================
  4. Introduction
  5. ~~~~~~~~~~~~
  6. Yes, the name sounds strange. "Kinematic Character". What is that?
  7. The reason for the name is that, when physics engines came out, they were called
  8. "Dynamics" engines (because they dealt mainly with collision
  9. responses). Many attempts were made to create a character controller
  10. using the dynamics engines, but it wasn't as easy as it seemed. Godot
  11. has one of the best implementations of dynamic character controller
  12. you can find (as it can be seen in the 2d/platformer demo), but using
  13. it requires a considerable level of skill and understanding of
  14. physics engines (or a lot of patience with trial and error).
  15. Some physics engines, such as Havok seem to swear by dynamic character
  16. controllers as the best option, while others (PhysX) would rather
  17. promote the kinematic one.
  18. So, what is the difference?:
  19. - A **dynamic character controller** uses a rigid body with an infinite
  20. inertia tensor. It's a rigid body that can't rotate.
  21. Physics engines always let objects move and collide, then solve their
  22. collisions all together. This makes dynamic character controllers
  23. able to interact with other physics objects seamlessly, as seen in
  24. the platformer demo. However, these interactions are not always
  25. predictable. Collisions can take more than one frame to be
  26. solved, so a few collisions may seem to displace a tiny bit. Those
  27. problems can be fixed, but require a certain amount of skill.
  28. - A **kinematic character controller** is assumed to always begin in a
  29. non-colliding state, and will always move to a non-colliding state.
  30. If it starts in a colliding state, it will try to free itself like
  31. rigid bodies do, but this is the exception, not the rule. This makes
  32. their control and motion a lot more predictable and easier to
  33. program. However, as a downside, they can't directly interact with
  34. other physics objects, unless done by hand in code.
  35. This short tutorial focuses on the kinematic character controller.
  36. It uses the old-school way of handling collisions, which is not
  37. necessarily simpler under the hood, but well hidden and presented as an API.
  38. Physics process
  39. ~~~~~~~~~~~~~~~
  40. To manage the logic of a kinematic body or character, it is always
  41. advised to use physics process, because it's called before physics step and its execution is
  42. in sync with physics server, also it is called the same amount of times
  43. per second, always. This makes physics and motion calculation work in a
  44. more predictable way than using regular process, which might have spikes
  45. or lose precision if the frame rate is too high or too low.
  46. .. tabs::
  47. .. code-tab:: gdscript GDScript
  48. extends CharacterBody2D
  49. func _physics_process(delta):
  50. pass
  51. .. code-tab:: csharp
  52. using Godot;
  53. public partial class MyCharacterBody2D : CharacterBody2D
  54. {
  55. public override void _PhysicsProcess(double delta)
  56. {
  57. }
  58. }
  59. Scene setup
  60. ~~~~~~~~~~~
  61. To have something to test, here's the scene (from the tilemap tutorial):
  62. `kinematic_character_2d_starter.zip <https://github.com/godotengine/godot-docs-project-starters/releases/download/latest-4.x/kinematic_character_2d_starter.zip>`_.
  63. We'll be creating a new scene for the character. Use the robot sprite and
  64. create a scene like this:
  65. .. image:: img/kbscene.webp
  66. You'll notice that there's a warning icon next to our CollisionShape2D node;
  67. that's because we haven't defined a shape for it. Create a new CircleShape2D
  68. in the shape property of CollisionShape2D. Click on <CircleShape2D> to go to the
  69. options for it, and set the radius to 30:
  70. .. image:: img/kbradius.webp
  71. **Note: As mentioned before in the physics tutorial, the physics engine
  72. can't handle scale on most types of shapes (only collision polygons,
  73. planes and segments work), so always change the parameters (such as
  74. radius) of the shape instead of scaling it. The same is also true for
  75. the kinematic/rigid/static bodies themselves, as their scale affects the
  76. shape scale.**
  77. Now, create a script for the character, the one used as an example
  78. above should work as a base.
  79. Finally, instance that character scene in the tilemap, and make the
  80. map scene the main one, so it runs when pressing play.
  81. .. image:: img/kbinstance.webp
  82. Moving the kinematic character
  83. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  84. Go back to the character scene, and open the script, the magic begins
  85. now! Kinematic body will do nothing by default, but it has a
  86. useful function called ``CharacterBody2D.move_and_collide()``.
  87. This function takes a :ref:`Vector2 <class_Vector2>` as
  88. an argument, and tries to apply that motion to the kinematic body. If a
  89. collision happens, it stops right at the moment of the collision.
  90. So, let's move our sprite downwards until it hits the floor:
  91. .. tabs::
  92. .. code-tab:: gdscript GDScript
  93. extends CharacterBody2D
  94. func _physics_process(delta):
  95. move_and_collide(Vector2(0, 1)) # Move down 1 pixel per physics frame
  96. .. code-tab:: csharp
  97. using Godot;
  98. public partial class MyCharacterBody2D : CharacterBody2D
  99. {
  100. public override void _PhysicsProcess(double delta)
  101. {
  102. // Move down 1 pixel per physics frame
  103. MoveAndCollide(new Vector2(0, 1));
  104. }
  105. }
  106. The result is that the character will move, but stop right when
  107. hitting the floor. Pretty cool, huh?
  108. The next step will be adding gravity to the mix, this way it behaves a
  109. little more like a regular game character:
  110. .. tabs::
  111. .. code-tab:: gdscript GDScript
  112. extends CharacterBody2D
  113. const GRAVITY = 200.0
  114. func _physics_process(delta):
  115. velocity.y += delta * GRAVITY
  116. var motion = velocity * delta
  117. move_and_collide(motion)
  118. .. code-tab:: csharp
  119. using Godot;
  120. public partial class MyCharacterBody2D : CharacterBody2D
  121. {
  122. private const float Gravity = 200.0f;
  123. public override void _PhysicsProcess(double delta)
  124. {
  125. var velocity = Velocity;
  126. velocity.Y += (float)delta * Gravity;
  127. Velocity = velocity;
  128. var motion = velocity * (float)delta;
  129. MoveAndCollide(motion);
  130. }
  131. }
  132. Now the character falls smoothly. Let's make it walk to the sides, left
  133. and right when touching the directional keys. Remember that the values
  134. being used (for speed at least) are pixels/second.
  135. This adds basic support for walking when pressing left and right:
  136. .. tabs::
  137. .. code-tab:: gdscript GDScript
  138. extends CharacterBody2D
  139. const GRAVITY = 200.0
  140. const WALK_SPEED = 200
  141. func _physics_process(delta):
  142. velocity.y += delta * GRAVITY
  143. if Input.is_action_pressed("ui_left"):
  144. velocity.x = -WALK_SPEED
  145. elif Input.is_action_pressed("ui_right"):
  146. velocity.x = WALK_SPEED
  147. else:
  148. velocity.x = 0
  149. # "move_and_slide" already takes delta time into account.
  150. move_and_slide()
  151. .. code-tab:: csharp
  152. using Godot;
  153. public partial class MyCharacterBody2D : CharacterBody2D
  154. {
  155. private const float Gravity = 200.0f;
  156. private const int WalkSpeed = 200;
  157. public override void _PhysicsProcess(double delta)
  158. {
  159. var velocity = Velocity;
  160. velocity.Y += (float)delta * Gravity;
  161. if (Input.IsActionPressed("ui_left"))
  162. {
  163. velocity.X = -WalkSpeed;
  164. }
  165. else if (Input.IsActionPressed("ui_right"))
  166. {
  167. velocity.X = WalkSpeed;
  168. }
  169. else
  170. {
  171. velocity.X = 0;
  172. }
  173. Velocity = velocity;
  174. // "MoveAndSlide" already takes delta time into account.
  175. MoveAndSlide();
  176. }
  177. }
  178. And give it a try.
  179. This is a good starting point for a platformer. A more complete demo can be found in the demo zip distributed with the
  180. engine, or in the
  181. https://github.com/godotengine/godot-demo-projects/tree/master/2d/kinematic_character.