2d_movement.rst 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. .. _doc_2d_movement:
  2. 2D movement overview
  3. ====================
  4. Introduction
  5. ------------
  6. Every beginner has been there: "How do I move my character?" Depending on the
  7. style of game you're making, you may have special requirements, but in general
  8. the movement in most 2D games is based on a small number of designs.
  9. We'll use :ref:`KinematicBody2D <class_KinematicBody2D>` for these examples,
  10. but the principles will apply to other node types (Area2D, RigidBody2D) as well.
  11. .. _doc_2d_movement_setup:
  12. Setup
  13. -----
  14. Each example below uses the same scene setup. Start with a ``KinematicBody2D`` with two
  15. children: ``Sprite`` and ``CollisionShape2D``. You can use the Godot icon ("icon.png")
  16. for the Sprite's texture or use any other 2D image you have.
  17. Open ``Project -> Project Settings`` and select the "Input Map" tab. Add the following
  18. input actions (see :ref:`InputEvent <doc_inputevent>` for details):
  19. .. image:: img/movement_inputs.png
  20. 8-way movement
  21. --------------
  22. In this scenario, you want the user to press the four directional keys (up/left/down/right
  23. or W/A/S/D) and move in the selected direction. The name "8-way movement" comes from the
  24. fact that the player can move diagonally by pressing two keys at the same time.
  25. .. image:: img/movement_8way.gif
  26. Add a script to the kinematic body and add the following code:
  27. .. tabs::
  28. .. code-tab:: gdscript GDScript
  29. extends KinematicBody2D
  30. export (int) var speed = 200
  31. var velocity = Vector2()
  32. func get_input():
  33. velocity = Vector2()
  34. if Input.is_action_pressed("right"):
  35. velocity.x += 1
  36. if Input.is_action_pressed("left"):
  37. velocity.x -= 1
  38. if Input.is_action_pressed("down"):
  39. velocity.y += 1
  40. if Input.is_action_pressed("up"):
  41. velocity.y -= 1
  42. velocity = velocity.normalized() * speed
  43. func _physics_process(delta):
  44. get_input()
  45. velocity = move_and_slide(velocity)
  46. .. code-tab:: csharp
  47. using Godot;
  48. using System;
  49. public class Movement : KinematicBody2D
  50. {
  51. [Export] public int speed = 200;
  52. public Vector2 velocity = new Vector2();
  53. public void GetInput()
  54. {
  55. velocity = new Vector2();
  56. if (Input.IsActionPressed("right"))
  57. velocity.x += 1;
  58. if (Input.IsActionPressed("left"))
  59. velocity.x -= 1;
  60. if (Input.IsActionPressed("down"))
  61. velocity.y += 1;
  62. if (Input.IsActionPressed("up"))
  63. velocity.y -= 1;
  64. velocity = velocity.Normalized() * speed;
  65. }
  66. public override void _PhysicsProcess(float delta)
  67. {
  68. GetInput();
  69. velocity = MoveAndSlide(velocity);
  70. }
  71. }
  72. In the ``get_input()`` function, we check for the four key events and sum them
  73. up to get the velocity vector. This has the benefit of making two opposite keys
  74. cancel each other out, but will also result in diagonal movement being faster
  75. due to the two directions being added together.
  76. We can prevent that if we *normalize* the velocity, which means we set
  77. its *length* to ``1``, and multiply by the desired speed.
  78. .. tip:: If you've never used vector math before, or need a refresher,
  79. you can see an explanation of vector usage in Godot at :ref:`doc_vector_math`.
  80. .. note::
  81. If the code above does nothing when you press the keys, double-check that
  82. you've set up input actions correctly as described in the
  83. :ref:`doc_2d_movement_setup` part of this tutorial.
  84. Rotation + movement
  85. -------------------
  86. This type of movement is sometimes called "Asteroids-style" because it resembles
  87. how that classic arcade game worked. Pressing left/right rotates the character,
  88. while up/down moves it forward or backward in whatever direction it's facing.
  89. .. image:: img/movement_rotate1.gif
  90. .. tabs::
  91. .. code-tab:: gdscript GDScript
  92. extends KinematicBody2D
  93. export (int) var speed = 200
  94. export (float) var rotation_speed = 1.5
  95. var velocity = Vector2()
  96. var rotation_dir = 0
  97. func get_input():
  98. rotation_dir = 0
  99. velocity = Vector2()
  100. if Input.is_action_pressed("right"):
  101. rotation_dir += 1
  102. if Input.is_action_pressed("left"):
  103. rotation_dir -= 1
  104. if Input.is_action_pressed("down"):
  105. velocity = Vector2(-speed, 0).rotated(rotation)
  106. if Input.is_action_pressed("up"):
  107. velocity = Vector2(speed, 0).rotated(rotation)
  108. func _physics_process(delta):
  109. get_input()
  110. rotation += rotation_dir * rotation_speed * delta
  111. velocity = move_and_slide(velocity)
  112. .. code-tab:: csharp
  113. using Godot;
  114. using System;
  115. public class Movement : KinematicBody2D
  116. {
  117. [Export] public int speed = 200;
  118. [Export] public float rotationSpeed = 1.5f;
  119. public Vector2 velocity = new Vector2();
  120. public int rotationDir = 0;
  121. public void GetInput()
  122. {
  123. rotationDir = 0;
  124. velocity = new Vector2();
  125. if (Input.IsActionPressed("right"))
  126. rotationDir += 1;
  127. if (Input.IsActionPressed("left"))
  128. rotationDir -= 1;
  129. if (Input.IsActionPressed("down"))
  130. velocity = new Vector2(-speed, 0).Rotated(Rotation);
  131. if (Input.IsActionPressed("up"))
  132. velocity = new Vector2(speed, 0).Rotated(Rotation);
  133. velocity = velocity.Normalized() * speed;
  134. }
  135. public override void _PhysicsProcess(float delta)
  136. {
  137. GetInput();
  138. Rotation += rotationDir * rotationSpeed * delta;
  139. velocity = MoveAndSlide(velocity);
  140. }
  141. }
  142. Here we've added two new variables to track our rotation direction and speed.
  143. Again, pressing both keys at once will cancel out and result in no rotation.
  144. The rotation is applied directly to the body's ``rotation`` property.
  145. To set the velocity, we use the ``Vector2.rotated()`` method, so that it points
  146. in the same direction as the body. ``rotated()`` is a useful vector function
  147. that you can use in many circumstances where you would otherwise need to apply
  148. trigonometric functions.
  149. Rotation + movement (mouse)
  150. ---------------------------
  151. This style of movement is a variation of the previous one. This time, the direction
  152. is set by the mouse position instead of the keyboard. The character will always
  153. "look at" the mouse pointer. The forward/back inputs remain the same, however.
  154. .. image:: img/movement_rotate2.gif
  155. .. tabs::
  156. .. code-tab:: gdscript GDScript
  157. extends KinematicBody2D
  158. export (int) var speed = 200
  159. var velocity = Vector2()
  160. func get_input():
  161. look_at(get_global_mouse_position())
  162. velocity = Vector2()
  163. if Input.is_action_pressed("down"):
  164. velocity = Vector2(-speed, 0).rotated(rotation)
  165. if Input.is_action_pressed("up"):
  166. velocity = Vector2(speed, 0).rotated(rotation)
  167. func _physics_process(delta):
  168. get_input()
  169. velocity = move_and_slide(velocity)
  170. .. code-tab:: csharp
  171. using Godot;
  172. using System;
  173. public class Movement : KinematicBody2D
  174. {
  175. [Export] public int speed = 200;
  176. public Vector2 velocity = new Vector2();
  177. public void GetInput()
  178. {
  179. LookAt(GetGlobalMousePosition());
  180. velocity = new Vector2();
  181. if (Input.IsActionPressed("down"))
  182. velocity = new Vector2(-speed, 0).Rotated(Rotation);
  183. if (Input.IsActionPressed("up"))
  184. velocity = new Vector2(speed, 0).Rotated(Rotation);
  185. velocity = velocity.Normalized() * speed;
  186. }
  187. public override void _PhysicsProcess(float delta)
  188. {
  189. GetInput();
  190. velocity = MoveAndSlide(velocity);
  191. }
  192. }
  193. Here we're using the :ref:`Node2D <class_Node2D>` ``look_at()`` method to
  194. point the player towards a given position. Without this function, you
  195. could get the same effect by setting the angle like this:
  196. .. tabs::
  197. .. code-tab:: gdscript GDScript
  198. rotation = get_global_mouse_position().angle_to_point(position)
  199. .. code-tab:: csharp
  200. var rotation = GetGlobalMousePosition().AngleToPoint(Position);
  201. Click-and-move
  202. --------------
  203. This last example uses only the mouse to control the character. Clicking
  204. on the screen will cause the player to move to the target location.
  205. .. image:: img/movement_click.gif
  206. .. tabs::
  207. .. code-tab:: gdscript GDScript
  208. extends KinematicBody2D
  209. export (int) var speed = 200
  210. var target = Vector2()
  211. var velocity = Vector2()
  212. func _input(event):
  213. if event.is_action_pressed("click"):
  214. target = get_global_mouse_position()
  215. func _physics_process(delta):
  216. velocity = position.direction_to(target) * speed
  217. # look_at(target)
  218. if position.distance_to(target) > 5:
  219. velocity = move_and_slide(velocity)
  220. .. code-tab:: csharp
  221. using Godot;
  222. using System;
  223. public class Movement : KinematicBody2D
  224. {
  225. [Export] public int speed = 200;
  226. public Vector2 target = new Vector2();
  227. public Vector2 velocity = new Vector2();
  228. public override void _Input(InputEvent @event)
  229. {
  230. if (@event.IsActionPressed("click"))
  231. {
  232. target = GetGlobalMousePosition();
  233. }
  234. }
  235. public override void _PhysicsProcess(float delta)
  236. {
  237. velocity = Position.DirectionTo(target) * speed;
  238. // LookAt(target);
  239. if (Position.DistanceTo(target) > 5)
  240. {
  241. velocity = MoveAndSlide(velocity);
  242. }
  243. }
  244. }
  245. Note the ``distance_to()`` check we make prior to movement. Without this test,
  246. the body would "jitter" upon reaching the target position, as it moves
  247. slightly past the position and tries to move back, only to move too far and
  248. repeat.
  249. Uncommenting the ``look_at()`` line will also turn the body to point in its
  250. direction of motion if you prefer.
  251. .. tip:: This technique can also be used as the basis of a "following" character.
  252. The ``target`` position can be that of any object you want to move to.
  253. Summary
  254. -------
  255. You may find these code samples useful as starting points for your own projects.
  256. Feel free to use them and experiment with them to see what you can make.
  257. You can download this sample project here:
  258. :download:`2D_movement_demo.zip <files/2D_movement_demo.zip>`