kinematic_character_2d.rst 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. Kinematic Character (2D)
  2. ========================
  3. Introduction
  4. ~~~~~~~~~~~~
  5. | Yes, the name sounds strange. "Kinematic Character" WTF is that? The
  6. reason is that when physics engines came out, they were called
  7. "Dynamics" engines (because they dealt mainly with collision
  8. responses). Many attempts were made to create a character controller
  9. using the dynamics engines but it wasn't as easy as it seems. Godot
  10. has one of the best implementations of dynamic character controller
  11. you can find (as it can be seen in the 2d/platformer demo), but using
  12. it requieres a considerable level of skill and understanding of
  13. physics engines (or a lot of patience with trial and error).
  14. | Some physics engines such as Havok seem to swear by dynamic character
  15. controllers as the best alternative, while others (PhysX) would rather
  16. promote the Kinematic one.
  17. So, what is really the difference?:
  18. - A **dynamic character controller** uses a rigid body with infinite
  19. inertial tensor. Basically, it's a rigid body that can't rotate.
  20. Physics engines always let objects collide, then solve their
  21. collisions all together. This makes dynamic character controllers
  22. able to interact with other physics objects seamlessly (as seen in
  23. the platformer demo), however these interactions are not always
  24. predictable. Collisions also can take more than one frame to be
  25. solved, so a few collisions may seem to displace a tiny bit. Those
  26. problems can be fixed, but require a certain amount of skill.
  27. - A **kinematic character controller** is assumed to always begin in a
  28. non-colliding state, and will always move to a non colliding state.
  29. If it starts in a colliding state, it will try to free itself (like
  30. rigid bodies do) but this is the exception, not the rule. This makes
  31. their control and motion a lot more predictable and easier to
  32. program. However, as a downside, they can't directly interact with
  33. other physics objects (unless done by hand in code).
  34. This short tutorial will focus on the kinematic character controller.
  35. Basically, the oldschool way of handling collisions (which is not
  36. necessarily simpler under the hood, but well hidden and presented as a
  37. nice and simple API).
  38. Fixed Process
  39. ~~~~~~~~~~~~~
  40. To manage the logic of a kinematic body or character, it is always
  41. advised to use fixed process, which is called the same amount of times
  42. per second, always. This makes physics and motion calculation work in a
  43. more predictable way than using regular process, which might have spikes
  44. or lose precision is the frame rate is too high or too low.
  45. ::
  46. extends KinematicBody2D
  47. func _fixed_process(delta):
  48. pass
  49. func _ready():
  50. set_fixed_process(true)
  51. Scene Setup
  52. ~~~~~~~~~~~
  53. To have something to test, here's the scene (from the tilemap tutorial)
  54. attachment:kbscene.zip. We'll be creating a new scene for the character.
  55. Use the robot sprite and create a scene like this:
  56. .. image:: /img/kbscene.png
  57. Let's add a circular collision shape to the collision body, create a new
  58. CircleShape2D in the shape property of CollisionShape2D. Set the radius
  59. to 30:
  60. .. image:: /img/kbradius.png
  61. **Note: As mentioned before in the physics tutorial, the physics engine
  62. can't handle scale on most types of shapes (only collision polygons,
  63. planes and segments work), so always change the parameters (such as
  64. radius) of the shape instead of scaling it. The same is also true for
  65. the kinematic/rigid/static bodies themselves, as their scale affect the
  66. shape scale.**
  67. | Now create a script for the character, the one used as an example
  68. above should work as a base.
  69. | Finally, instance that character scene in the tilemap, and make the
  70. map scene the main one, so it runs when pressing play.
  71. .. image:: /img/kbinstance.png
  72. Moving the Kinematic Character
  73. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  74. Go back to the character scene, and open the script, the magic begins
  75. now! Kinematic body will do nothing by default, but it has a really
  76. useful function called
  77. `move(motion\_vector:Vector2) <https://github.com/okamstudio/godot/wiki/class_kinematicbody2d#move>`__.
  78. This function takes a
  79. `Vector2 <https://github.com/okamstudio/godot/wiki/class_vector2>`__ as
  80. an argument, and tries to apply that motion to the kinematic body. If a
  81. collision happens, it stops right at the moment of the collision.
  82. So, let's move our sprite downwards until it hits the floor:
  83. ::
  84. extends KinematicBody2D
  85. func _fixed_process(delta):
  86. move( Vector2(0,1) ) #move down 1 pixel per physics frame
  87. func _ready():
  88. set_fixed_process(true)
  89. | The result is that the character will move, but stop right when
  90. hitting the floor. Pretty cool, huh?
  91. | The next step will be adding gravity to the mix, this way it behaves a
  92. little more like an actual game character:
  93. ::
  94. extends KinematicBody2D
  95. const GRAVITY = 200.0
  96. var velocity = Vector2()
  97. func _fixed_process(delta):
  98. velocity.y += delta * GRAVITY
  99. var motion = velocity * delta
  100. move( motion )
  101. func _ready():
  102. set_fixed_process(true)
  103. Now the character falls smoothly. Let's make it walk to the sides, left
  104. and right when touching the directional keys. Remember that the values
  105. being used (for speed at least) is pixels/second.
  106. This adds simple walking support by pressing left and right:
  107. ::
  108. extends KinematicBody2D
  109. const GRAVITY = 200.0
  110. const WALK_SPEED = 200
  111. var velocity = Vector2()
  112. func _fixed_process(delta):
  113. velocity.y += delta * GRAVITY
  114. if (Input.is_action_pressed("ui_left")):
  115. velocity.x = -WALK_SPEED
  116. elif (Input.is_action_pressed("ui_right")):
  117. velocity.x = WALK_SPEED
  118. else:
  119. velocity.x = 0
  120. var motion = velocity * delta
  121. move( motion )
  122. func _ready():
  123. set_fixed_process(true)
  124. And give it a try.
  125. Problem?
  126. ~~~~~~~~
  127. And.. it doesn't work very well. If you go to the left against a wall,
  128. it gets stuck unless you release the arrow key. Once it is on the floor,
  129. it also gets stuck and it won't walk. What is going on??
  130. The answer is, what it seems like it should be simple, it isn't that
  131. simple in reality. If the motion can't be completed, the character will
  132. stop moving. It's as simple as that. This diagram should illustrate
  133. better what is going on:
  134. .. image:: /img/motion_diagram.png
  135. Basically, the desired motion vector will never complete because it hits
  136. the floor and the wall too early in the motion trajectory and that makes
  137. it stop there. Remember that even though the character is on the floor,
  138. the gravity is always turning the motion vector downwards.
  139. Solution!
  140. ~~~~~~~~~
  141. The solution? This situation is solved by "sliding" by the collision
  142. normal. KinematicBody2D provides two useful functions:
  143. - `KinematicBody2D.is\_colliding() <https://github.com/okamstudio/godot/wiki/class_kinematicbody2d#is_colliding>`__
  144. - `KinematicBody2D.get\_collision\_normal() <https://github.com/okamstudio/godot/wiki/class_kinematicbody2d#get_collision_normal>`__
  145. So what we want to do is this:
  146. .. image:: /img/motion_reflect.png
  147. | When colliding, the function move() returns the "remainder" of the
  148. motion vector. That means, if the motion vector is 40 pixels, but
  149. collision happened at 10 pixels, the same vector but 30 pixels long is
  150. returned.
  151. | The correct way to solve the motion is, then, to slide by the normal
  152. this way:
  153. ::
  154. func _fixed_process(delta):
  155. velocity.y += delta * GRAVITY
  156. if (Input.is_action_pressed("ui_left")):
  157. velocity.x = - WALK_SPEED
  158. elif (Input.is_action_pressed("ui_right")):
  159. velocity.x = WALK_SPEED
  160. else:
  161. velocity.x = 0
  162. var motion = velocity * delta
  163. motion = move( motion )
  164. if (is_colliding()):
  165. var n = get_collision_normal()
  166. motion = n.slide( motion )
  167. velocity = n.slide( velocity )
  168. move( motion )
  169. func _ready():
  170. set_fixed_process(true)
  171. | Note that not only the motion has been modified but also the velocity.
  172. This makes sense as it helps keep
  173. | the new direction too.
  174. The normal can also be used to detect that the character is on floor, by
  175. checking the angle. If the normal points up (or at least, within a
  176. certain threshold), the character can be determined to be there.
  177. A more complete demo can be found in the demo zip distributed with the
  178. engine, or in the
  179. https://github.com/okamstudio/godot/tree/master/demos/2d/kinematic\_char.
  180. *Juan Linietsky, Ariel Manzur, Distributed under the terms of the `CC
  181. By <https://creativecommons.org/licenses/by/3.0/legalcode>`__ license.*