diff --git a/tutorials/2d/2d_movement.rst b/tutorials/2d/2d_movement.rst new file mode 100644 index 000000000..701aae49d --- /dev/null +++ b/tutorials/2d/2d_movement.rst @@ -0,0 +1,207 @@ +.. _doc_2d_movement: + +2D Movement Overview +==================== + +Introduction +------------ + +Every beginner has been there: "How do I move my character?" Depending on the +style of game you're making, you may have special requirements, but in general +the movement in most 2D games is based on a small number of + +We'll use :ref:`KinematicBody2D ` for these examples, +but the principles will apply to other node types (Area2D, RigidBody2D) as well. + +Setup +----- + +Each example below uses the same scene setup. Start with a ``KinematicBody2D`` with two +children: ``Sprite`` and ``CollisionShape2D``. You can use the Godot icon ("icon.png") +for the Sprite's texture or use any other 2D image you have available. + +Open ``Project -> Project Settings`` and select the "Input Map" tab. Add the following +input actions (see :ref:`InputEvent ` for details): + +.. image:: img/movement_inputs.png + +8-Way Movement +-------------- + +In this scenario, you want the user to press the four directional keys (up/left/down/right +or W/A/S/D) and move in the selected direction. The name "8-way movement" comes from the +fact that diagonal movement can be chosen by pressing two keys at once. + +.. image:: img/movement_8way.gif + +Add a script to the kinematic body and add the following code: + +:: + + extends KinematicBody2D + + export (int) var speed = 200 + + var velocity = Vector2() + + func get_input(): + velocity = Vector2() + if Input.is_action_pressed('right'): + velocity.x += 1 + if Input.is_action_pressed('left'): + velocity.x -= 1 + if Input.is_action_pressed('down'): + velocity.y += 1 + if Input.is_action_pressed('up'): + velocity.y -= 1 + velocity = velocity.normalized() * speed + + func _physics_process(delta): + get_input() + move_and_slide(velocity) + +In the ``get_input()`` function we check for the four key events and sum them +up to get the velocity vector. This has the benefit of making two opposite keys +cancel each other out, but will also result in diagonal movement being faster +due to the two directions being added together. + +We can prevent that if we *normalize* the velocity, which means we set +its *length* to ``1``, and multiply by the desired speed. + +.. tip:: If you've never used vector math before, or just need a refresher, + you can see an explanation of vector usage in Godot at :ref:`doc_vector_math`. + + + + + +Rotation + Movement +------------------- + +This type of movement is sometimes called "Asteroids-style" because it resembles +how that classic arcade game worked. Pressing left/right rotates the character, +while up/down moves it forward or backward in whatever direction it's facing. + +.. image:: img/movement_rotate1.gif + +:: + + extends KinematicBody2D + + export (int) var speed = 200 + export (float) var rot_speed = 1.5 + + var velocity = Vector2() + var rot_dir = 0 + + func get_input(): + rot_dir = 0 + velocity = Vector2() + if Input.is_action_pressed('right'): + rot_dir += 1 + if Input.is_action_pressed('left'): + rot_dir -= 1 + if Input.is_action_pressed('down'): + velocity = Vector2(-speed, 0).rotated(rotation) + if Input.is_action_pressed('up'): + velocity = Vector2(speed, 0).rotated(rotation) + + func _physics_process(delta): + get_input() + rotation += rot_dir * rot_speed * delta + move_and_slide(velocity) + +Here we've added two new variables to track our rotation direction and speed. +Again, pressing both keys at once will cancel out and result in no rotation. +The rotation is applied directly to the body's ``rotation`` property. + +To set the velocity, we use the ``Vector2.rotated()`` method so that it points +in the same direction as the body. ``rotated()`` is a very useful vector function +that you can use in many circumstances where you would otherwise need to apply +trigonometric functions. + +Rotation + Movement (mouse) +--------------------------- + +This style of movement is a variation of the previous one. This time, the direction +is set by the mouse position instead of the keyboard. The character will always +"look at" the mouse pointer. The forward/back inputs remain the same, however. + +.. image:: img/movement_rotate2.gif + +:: + + extends KinematicBody2D + + export (int) var speed = 200 + + var velocity = Vector2() + + func get_input(): + look_at(get_global_mouse_position()) + velocity = Vector2() + if Input.is_action_pressed('down'): + velocity = Vector2(-speed, 0).rotated(rotation) + if Input.is_action_pressed('up'): + velocity = Vector2(speed, 0).rotated(rotation) + + func _physics_process(delta): + get_input() + move_and_slide(velocity) + +Note that the :ref:`Node2D ` ``look_at()`` method exists just +for this purpose. Without this function, you could get the same effect by +setting the angle like this: + +:: + + rotation = get_global_mouse_position().angle_to_point(position) + + +Click-and-Move +-------------- + +This last example uses only the mouse to control the character. Clicking +on the screen will cause the player to move to the target location. + +.. image:: img/movement_click.gif + +:: + + extends KinematicBody2D + + export (int) var speed = 200 + + var target = Vector2() + var velocity = Vector2() + + func _input(event): + if event.is_action_pressed('click'): + target = get_global_mouse_position() + + func _physics_process(delta): + velocity = (target - position).normalized() * speed + # rotation = velocity.angle() + if (target - position).length() > 5: + move_and_slide(velocity) + + +Note the ``length()`` check we make prior to movement. Without this test, +the body would "jitter" upon reaching the target position, as it moves +slightly past the position and tries to move back, only to move too far and +repeat. + +Uncommenting the ``rotation`` line will also turn the body to point in its +direction of motion if you prefer. + +.. tip:: This technique can also be used as the basis of a "following" character. + The ``target`` position can be that of any object you want to move to. + +Summary +------- + +You may find these code samples useful as starting points for your own projects. +Feel free to use them and experiment with them to see what you can make. + +You can download this sample project here: +:download:`2D_movement_demo.zip ` diff --git a/tutorials/2d/files/2D_movement_demo.zip b/tutorials/2d/files/2D_movement_demo.zip new file mode 100644 index 000000000..cc591e02f Binary files /dev/null and b/tutorials/2d/files/2D_movement_demo.zip differ diff --git a/tutorials/2d/img/movement_8way.gif b/tutorials/2d/img/movement_8way.gif new file mode 100644 index 000000000..b1c94455b Binary files /dev/null and b/tutorials/2d/img/movement_8way.gif differ diff --git a/tutorials/2d/img/movement_click.gif b/tutorials/2d/img/movement_click.gif new file mode 100644 index 000000000..6aa2a3cf0 Binary files /dev/null and b/tutorials/2d/img/movement_click.gif differ diff --git a/tutorials/2d/img/movement_inputs.png b/tutorials/2d/img/movement_inputs.png new file mode 100644 index 000000000..e16c994bd Binary files /dev/null and b/tutorials/2d/img/movement_inputs.png differ diff --git a/tutorials/2d/img/movement_rotate1.gif b/tutorials/2d/img/movement_rotate1.gif new file mode 100644 index 000000000..975cd3359 Binary files /dev/null and b/tutorials/2d/img/movement_rotate1.gif differ diff --git a/tutorials/2d/img/movement_rotate2.gif b/tutorials/2d/img/movement_rotate2.gif new file mode 100644 index 000000000..376ff6a56 Binary files /dev/null and b/tutorials/2d/img/movement_rotate2.gif differ diff --git a/tutorials/2d/index.rst b/tutorials/2d/index.rst index 467d1f76e..e435bf84e 100644 --- a/tutorials/2d/index.rst +++ b/tutorials/2d/index.rst @@ -10,3 +10,4 @@ using_tilemaps custom_drawing_in_2d particle_systems_2d + 2d_movement