|
@@ -1,7 +1,7 @@
|
|
|
.. _doc_using_character_body_2d:
|
|
|
|
|
|
-Using CharacterBody2D
|
|
|
-=====================
|
|
|
+Using CharacterBody2D/3D
|
|
|
+========================
|
|
|
|
|
|
Introduction
|
|
|
------------
|
|
@@ -13,8 +13,8 @@ works and what their pros and cons are. In this tutorial, we'll look at the
|
|
|
:ref:`CharacterBody2D <class_CharacterBody2D>` node and show some examples
|
|
|
of how to use it.
|
|
|
|
|
|
-.. note:: This document assumes you're familiar with Godot's various physics
|
|
|
- bodies. Please read :ref:`doc_physics_introduction` first.
|
|
|
+.. note:: While this document uses ``CharacterBody2D`` in its examples, the same
|
|
|
+ concepts apply in 3D as well.
|
|
|
|
|
|
What is a character body?
|
|
|
-------------------------
|
|
@@ -25,6 +25,10 @@ engine physics properties, like gravity or friction. While this means that you
|
|
|
have to write some code to create their behavior, it also means you have more
|
|
|
precise control over how they move and react.
|
|
|
|
|
|
+.. note:: This document assumes you're familiar with Godot's various physics
|
|
|
+ bodies. Please read :ref:`doc_physics_introduction` first, for an overview
|
|
|
+ of the physics options.
|
|
|
+
|
|
|
.. tip:: A `CharacterBody2D` can be affected by gravity and other forces,
|
|
|
but you must calculate the movement in code. The physics engine will
|
|
|
not move a `CharacterBody2D`.
|
|
@@ -34,20 +38,18 @@ Movement and collision
|
|
|
|
|
|
When moving a ``CharacterBody2D``, you should not set its ``position`` property
|
|
|
directly. Instead, you use the ``move_and_collide()`` or ``move_and_slide()`` methods.
|
|
|
-These methods move the body along a given vector and instantly stop if
|
|
|
-a collision is detected with another body. After a CharacterBody2D has collided,
|
|
|
-any *collision response* must be coded manually.
|
|
|
+These methods move the body along a given vector and detect collisions.
|
|
|
|
|
|
-.. warning:: You should only do Kinematic body movement in the ``_physics_process()`` callback.
|
|
|
+.. warning:: You should handle physics body movement in the ``_physics_process()`` callback.
|
|
|
|
|
|
The two movement methods serve different purposes, and later in this tutorial, you'll
|
|
|
see examples of how they work.
|
|
|
|
|
|
-``move_and_collide``
|
|
|
-~~~~~~~~~~~~~~~~~~~~
|
|
|
+move_and_collide
|
|
|
+~~~~~~~~~~~~~~~~
|
|
|
|
|
|
-This method takes one parameter: a :ref:`Vector2 <class_Vector2>` indicating the body's
|
|
|
-relative movement. Typically, this is your velocity vector multiplied by the
|
|
|
+This method takes one required parameter: a :ref:`Vector2 <class_Vector2>` indicating
|
|
|
+the body's relative movement. Typically, this is your velocity vector multiplied by the
|
|
|
frame timestep (``delta``). If the engine detects a collision anywhere along
|
|
|
this vector, the body will immediately stop moving. If this happens, the
|
|
|
method will return a :ref:`KinematicCollision2D <class_KinematicCollision2D>` object.
|
|
@@ -56,8 +58,13 @@ method will return a :ref:`KinematicCollision2D <class_KinematicCollision2D>` ob
|
|
|
and the colliding object. Using this data, you can calculate your collision
|
|
|
response.
|
|
|
|
|
|
-``move_and_slide``
|
|
|
-~~~~~~~~~~~~~~~~~~
|
|
|
+``move_and_collide`` is most useful when you just want to move the body and
|
|
|
+detect collision, but don't need any automatic collision response. For example,
|
|
|
+if you need a bullet that ricochets off a wall, you can directly change the angle
|
|
|
+of the velocity when you detect a collision. See below for an example.
|
|
|
+
|
|
|
+move_and_slide
|
|
|
+~~~~~~~~~~~~~~
|
|
|
|
|
|
The ``move_and_slide()`` method is intended to simplify the collision
|
|
|
response in the common case where you want one body to slide along the other.
|
|
@@ -67,44 +74,47 @@ It is especially useful in platformers or top-down games, for example.
|
|
|
using ``delta``. Do *not* multiply your velocity vector by ``delta``
|
|
|
before passing it to ``move_and_slide()``.
|
|
|
|
|
|
-In addition to the velocity vector, ``move_and_slide()`` takes a number of
|
|
|
-other parameters allowing you to customize the slide behavior:
|
|
|
+When calling ``move_and_slide()``, the function uses a number of node properties
|
|
|
+to calculate its slide behavior. These properties can be found in the Inspector,
|
|
|
+or set in code.
|
|
|
|
|
|
-- ``up_direction`` - *default value:* ``Vector2( 0, 0 )``
|
|
|
+- ``velocity`` - *default value:* ``Vector2( 0, 0 )``
|
|
|
|
|
|
- This parameter allows you to define what surfaces the engine should consider
|
|
|
- being the floor. Setting this lets you use the ``is_on_floor()``, ``is_on_wall()``,
|
|
|
- and ``is_on_ceiling()`` methods to detect what type of surface the body is
|
|
|
- in contact with. The default value means that all surfaces are considered walls.
|
|
|
+ This property represents the body's velocity vector in pixels per second.
|
|
|
+ ``move_and_slide()`` will modify this value automatically when colliding.
|
|
|
|
|
|
-- ``stop_on_slope`` - *default value:* ``false``
|
|
|
+- ``motion_mode`` - *default value:* ``MOTION_MODE_GROUNDED``
|
|
|
|
|
|
- This parameter prevents a body from sliding down slopes when standing still.
|
|
|
+ This property is typically used to distinguish between side-scrolling and
|
|
|
+ top-down movement. When using the default value, you can use the ``is_on_floor()``,
|
|
|
+ ``is_on_wall()``, and ``is_on_ceiling()`` methods to detect what type of
|
|
|
+ surface the body is in contact with, and the body will interact with slopes.
|
|
|
+ When using ``MOTION_MODE_FLOATING``, all collisions will be considered "walls".
|
|
|
|
|
|
-- ``max_slides`` - *default value:* ``4``
|
|
|
+- ``up_direction`` - *default value:* ``Vector2( 0, -1 )``
|
|
|
|
|
|
- This parameter is the maximum number of collisions before the body stops moving. Setting
|
|
|
- it too low may prevent movement entirely.
|
|
|
+ This property allows you to define what surfaces the engine should consider
|
|
|
+ being the floor. Its value lets you use the ``is_on_floor()``, ``is_on_wall()``,
|
|
|
+ and ``is_on_ceiling()`` methods to detect what type of surface the body is
|
|
|
+ in contact with. The default value means that the top side of horizontal surfaces
|
|
|
+ will be considered "ground".
|
|
|
|
|
|
-- ``floor_max_angle`` - *default value:* ``0.785398`` (in radians, equivalent to ``45`` degrees)
|
|
|
+- ``floor_stop_on_slope`` - *default value:* ``true``
|
|
|
|
|
|
- This parameter is the maximum angle before a surface is no longer considered a "floor."
|
|
|
+ This parameter prevents a body from sliding down slopes when standing still.
|
|
|
|
|
|
-- ``infinite_inertia`` - *default value:* ``true``
|
|
|
+- ``wall_min_slide_angle`` - *default value:* ``0.261799`` (in radians, equivalent to ``15`` degrees)
|
|
|
|
|
|
- When this parameter is ``true``, the body can push :ref:`RigidBody2D <class_RigidBody2D>`
|
|
|
- nodes, ignoring their mass, but won't detect collisions with them. If it's ``false``
|
|
|
- the body will collide with rigid bodies and stop.
|
|
|
+ This is the minimum angle where the body is allowed to slide when it hits a
|
|
|
+ slope.
|
|
|
|
|
|
-``move_and_slide_with_snap``
|
|
|
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
+- ``floor_max_angle`` - *default value:* ``0.785398`` (in radians, equivalent to ``45`` degrees)
|
|
|
|
|
|
-This method adds some additional functionality to ``move_and_slide()`` by adding
|
|
|
-the ``snap`` parameter. As long as this vector is in contact with the ground, the
|
|
|
-body will remain attached to the surface. Note that this means you must disable
|
|
|
-snapping when jumping, for example. You can do this either by setting ``snap``
|
|
|
-to ``Vector2.ZERO`` or by using ``move_and_slide()`` instead.
|
|
|
+ This parameter is the maximum angle before a surface is no longer considered a "floor."
|
|
|
|
|
|
+There are many other properties that can be used to modify the body's behavior under
|
|
|
+specific circumstances. See the :ref:`CharacterBody2D <class_CharacterBody2D>` docs
|
|
|
+for full details.
|
|
|
|
|
|
Detecting collisions
|
|
|
--------------------
|
|
@@ -113,7 +123,7 @@ When using ``move_and_collide()`` the function returns a ``KinematicCollision2D`
|
|
|
directly, and you can use this in your code.
|
|
|
|
|
|
When using ``move_and_slide()`` it's possible to have multiple collisions occur,
|
|
|
-as the slide response is calculated. To process these collisions, use ``get_slide_count()``
|
|
|
+as the slide response is calculated. To process these collisions, use ``get_slide_collision_count()``
|
|
|
and ``get_slide_collision()``:
|
|
|
|
|
|
.. tabs::
|
|
@@ -122,32 +132,32 @@ and ``get_slide_collision()``:
|
|
|
# Using move_and_collide.
|
|
|
var collision = move_and_collide(velocity * delta)
|
|
|
if collision:
|
|
|
- print("I collided with ", collision.collider.name)
|
|
|
+ print("I collided with ", collision.get_collider().name)
|
|
|
|
|
|
# Using move_and_slide.
|
|
|
- velocity = move_and_slide(velocity)
|
|
|
+ move_and_slide()
|
|
|
for i in get_slide_count():
|
|
|
var collision = get_slide_collision(i)
|
|
|
- print("I collided with ", collision.collider.name)
|
|
|
+ print("I collided with ", collision.get_collider().name)
|
|
|
|
|
|
.. code-tab:: csharp
|
|
|
|
|
|
// Using MoveAndCollide.
|
|
|
- var collision = MoveAndCollide(velocity * delta);
|
|
|
+ var collision = MoveAndCollide(Velocity * (float)delta);
|
|
|
if (collision != null)
|
|
|
{
|
|
|
- GD.Print("I collided with ", ((Node)collision.Collider).Name);
|
|
|
+ GD.Print("I collided with ", ((Node)collision.GetCollider()).Name);
|
|
|
}
|
|
|
|
|
|
// Using MoveAndSlide.
|
|
|
- velocity = MoveAndSlide(velocity);
|
|
|
+ MoveAndSlide();
|
|
|
for (int i = 0; i < GetSlideCount(); i++)
|
|
|
{
|
|
|
var collision = GetSlideCollision(i);
|
|
|
- GD.Print("I collided with ", ((Node)collision.Collider).Name);
|
|
|
+ GD.Print("I collided with ", ((Node)collision.GetCollider()).Name);
|
|
|
}
|
|
|
|
|
|
-.. note:: `get_slide_count()` only counts times the body has collided and changed direction.
|
|
|
+.. note:: `get_slide_collision_count()` only counts times the body has collided and changed direction.
|
|
|
|
|
|
See :ref:`KinematicCollision2D <class_KinematicCollision2D>` for details on what
|
|
|
collision data is returned.
|
|
@@ -170,29 +180,29 @@ the same collision response:
|
|
|
# using move_and_collide
|
|
|
var collision = move_and_collide(velocity * delta)
|
|
|
if collision:
|
|
|
- velocity = velocity.slide(collision.normal)
|
|
|
+ velocity = velocity.slide(collision.get_normal())
|
|
|
|
|
|
# using move_and_slide
|
|
|
- velocity = move_and_slide(velocity)
|
|
|
+ move_and_slide()
|
|
|
|
|
|
.. code-tab:: csharp
|
|
|
|
|
|
// using MoveAndCollide
|
|
|
- var collision = MoveAndCollide(velocity * delta);
|
|
|
+ var collision = MoveAndCollide(Velocity * (float)delta);
|
|
|
if (collision != null)
|
|
|
{
|
|
|
- velocity = velocity.Slide(collision.Normal);
|
|
|
+ velocity = velocity.Slide(collision.GetNormal());
|
|
|
}
|
|
|
// using MoveAndSlide
|
|
|
- velocity = MoveAndSlide(velocity);
|
|
|
+ MoveAndSlide();
|
|
|
|
|
|
Anything you do with ``move_and_slide()`` can also be done with ``move_and_collide()``,
|
|
|
but it might take a little more code. However, as we'll see in the examples below,
|
|
|
there are cases where ``move_and_slide()`` doesn't provide the response you want.
|
|
|
|
|
|
-In the example above, we assign the velocity that ``move_and_slide()`` returns
|
|
|
-back into the ``velocity`` variable. This is because when the character collides
|
|
|
-with the environment, the function recalculates the speed internally to reflect
|
|
|
+In the example above, ``move_and_slide()`` automatically alters the ``velocity``
|
|
|
+variable. This is because when the character collides with the environment,
|
|
|
+the function recalculates the speed internally to reflect
|
|
|
the slowdown.
|
|
|
|
|
|
For example, if your character fell on the floor, you don't want it to
|
|
@@ -201,9 +211,8 @@ vertical speed to reset to zero.
|
|
|
|
|
|
``move_and_slide()`` may also recalculate the kinematic body's velocity several
|
|
|
times in a loop as, to produce a smooth motion, it moves the character and
|
|
|
-collides up to five times by default. At the end of the process, the function
|
|
|
-returns the character's new velocity that we can store in our ``velocity``
|
|
|
-variable, and use on the next frame.
|
|
|
+collides up to five times by default. At the end of the process, the character's
|
|
|
+new velocity is available for use on the next frame.
|
|
|
|
|
|
Examples
|
|
|
--------
|
|
@@ -231,21 +240,11 @@ Attach a script to the CharacterBody2D and add the following code:
|
|
|
|
|
|
extends CharacterBody2D
|
|
|
|
|
|
- var speed = 250
|
|
|
- var velocity = Vector2()
|
|
|
+ var speed = 300
|
|
|
|
|
|
func get_input():
|
|
|
- # Detect up/down/left/right keystate and only move when pressed.
|
|
|
- velocity = Vector2()
|
|
|
- if Input.is_action_pressed('ui_right'):
|
|
|
- velocity.x += 1
|
|
|
- if Input.is_action_pressed('ui_left'):
|
|
|
- velocity.x -= 1
|
|
|
- if Input.is_action_pressed('ui_down'):
|
|
|
- velocity.y += 1
|
|
|
- if Input.is_action_pressed('ui_up'):
|
|
|
- velocity.y -= 1
|
|
|
- velocity = velocity.normalized() * speed
|
|
|
+ var input_dir = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
|
|
|
+ velocity = input_dir * speed
|
|
|
|
|
|
func _physics_process(delta):
|
|
|
get_input()
|
|
@@ -255,34 +254,20 @@ Attach a script to the CharacterBody2D and add the following code:
|
|
|
|
|
|
using Godot;
|
|
|
|
|
|
- public partial class KBExample : CharacterBody2D
|
|
|
+ public partial class CBExample : CharacterBody2D
|
|
|
{
|
|
|
- public int Speed = 250;
|
|
|
- private Vector2 _velocity = new Vector2();
|
|
|
+ public int Speed = 300;
|
|
|
|
|
|
public void GetInput()
|
|
|
{
|
|
|
- // Detect up/down/left/right keystate and only move when pressed
|
|
|
- _velocity = new Vector2();
|
|
|
-
|
|
|
- if (Input.IsActionPressed("ui_right"))
|
|
|
- _velocity.x += 1;
|
|
|
-
|
|
|
- if (Input.IsActionPressed("ui_left"))
|
|
|
- _velocity.x -= 1;
|
|
|
-
|
|
|
- if (Input.IsActionPressed("ui_down"))
|
|
|
- _velocity.y += 1;
|
|
|
-
|
|
|
- if (Input.IsActionPressed("ui_up"))
|
|
|
- _velocity.y -= 1;
|
|
|
- _velocity = _velocity.Normalized() * Speed;
|
|
|
+ Vector2 inputDir = Input.GetVector("ui_left", "ui_right", "ui_up", "ui_down");
|
|
|
+ Velocity = inputDir * Speed;
|
|
|
}
|
|
|
|
|
|
- public override void _PhysicsProcess(float delta)
|
|
|
+ public override void _PhysicsProcess(double delta)
|
|
|
{
|
|
|
GetInput();
|
|
|
- MoveAndCollide(_velocity * delta);
|
|
|
+ MoveAndCollide(Velocity * (float)delta);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -301,8 +286,7 @@ This happens because there is no *collision response*. ``move_and_collide()`` st
|
|
|
the body's movement when a collision occurs. We need to code whatever response we
|
|
|
want from the collision.
|
|
|
|
|
|
-Try changing the function to ``move_and_slide(velocity)`` and running again.
|
|
|
-Note that we removed ``delta`` from the velocity calculation.
|
|
|
+Try changing the function to ``move_and_slide()`` and running again.
|
|
|
|
|
|
``move_and_slide()`` provides a default collision response of sliding the body along the
|
|
|
collision object. This is useful for a great many game types, and may be all you need
|
|
@@ -328,23 +312,19 @@ uses the mouse pointer. Here is the code for the Player, using ``move_and_slide(
|
|
|
|
|
|
var Bullet = preload("res://Bullet.tscn")
|
|
|
var speed = 200
|
|
|
- var velocity = Vector2()
|
|
|
|
|
|
func get_input():
|
|
|
# Add these actions in Project Settings -> Input Map.
|
|
|
- velocity = Vector2()
|
|
|
- if Input.is_action_pressed('backward'):
|
|
|
- velocity = Vector2(-speed/3, 0).rotated(rotation)
|
|
|
- if Input.is_action_pressed('forward'):
|
|
|
- velocity = Vector2(speed, 0).rotated(rotation)
|
|
|
- if Input.is_action_just_pressed('mouse_click'):
|
|
|
+ var input_dir = Input.get_axis("backward", "forward")
|
|
|
+ velocity = transform.x * input_dir * speed
|
|
|
+ if Input.is_action_just_pressed("shoot"):
|
|
|
shoot()
|
|
|
|
|
|
func shoot():
|
|
|
# "Muzzle" is a Marker2D placed at the barrel of the gun.
|
|
|
var b = Bullet.instantiate()
|
|
|
b.start($Muzzle.global_position, rotation)
|
|
|
- get_parent().add_child(b)
|
|
|
+ get_tree().root.add_child(b)
|
|
|
|
|
|
func _physics_process(delta):
|
|
|
get_input()
|
|
@@ -352,31 +332,23 @@ uses the mouse pointer. Here is the code for the Player, using ``move_and_slide(
|
|
|
# Don't move if too close to the mouse pointer.
|
|
|
if dir.length() > 5:
|
|
|
rotation = dir.angle()
|
|
|
- velocity = move_and_slide(velocity)
|
|
|
+ move_and_slide()
|
|
|
|
|
|
.. code-tab:: csharp
|
|
|
|
|
|
using Godot;
|
|
|
|
|
|
- public partial class KBExample : CharacterBody2D
|
|
|
+ public partial class CBExample : CharacterBody2D
|
|
|
{
|
|
|
private PackedScene _bullet = (PackedScene)GD.Load("res://Bullet.tscn");
|
|
|
public int Speed = 200;
|
|
|
- private Vector2 _velocity = new Vector2();
|
|
|
|
|
|
public void GetInput()
|
|
|
{
|
|
|
- // add these actions in Project Settings -> Input Map
|
|
|
- _velocity = new Vector2();
|
|
|
- if (Input.IsActionPressed("backward"))
|
|
|
- {
|
|
|
- _velocity = new Vector2(-Speed/3, 0).Rotated(Rotation);
|
|
|
- }
|
|
|
- if (Input.IsActionPressed("forward"))
|
|
|
- {
|
|
|
- _velocity = new Vector2(Speed, 0).Rotated(Rotation);
|
|
|
- }
|
|
|
- if (Input.IsActionPressed("mouse_click"))
|
|
|
+ // Add these actions in Project Settings -> Input Map.
|
|
|
+ float inputDir = Input.GetAxis("backward", "forward");
|
|
|
+ Velocity = Transform.x * inputDir * Speed;
|
|
|
+ if (Input.IsActionPressed("shoot"))
|
|
|
{
|
|
|
Shoot();
|
|
|
}
|
|
@@ -384,21 +356,21 @@ uses the mouse pointer. Here is the code for the Player, using ``move_and_slide(
|
|
|
|
|
|
public void Shoot()
|
|
|
{
|
|
|
- // "Muzzle" is a Marker2D placed at the barrel of the gun
|
|
|
+ // "Muzzle" is a Marker2D placed at the barrel of the gun.
|
|
|
var b = (Bullet)_bullet.Instantiate();
|
|
|
b.Start(GetNode<Node2D>("Muzzle").GlobalPosition, Rotation);
|
|
|
- GetParent().AddChild(b);
|
|
|
+ GetTree().Root.AddChild(b);
|
|
|
}
|
|
|
|
|
|
- public override void _PhysicsProcess(float delta)
|
|
|
+ public override void _PhysicsProcess(double delta)
|
|
|
{
|
|
|
GetInput();
|
|
|
var dir = GetGlobalMousePosition() - GlobalPosition;
|
|
|
- // Don't move if too close to the mouse pointer
|
|
|
+ // Don't move if too close to the mouse pointer.
|
|
|
if (dir.Length() > 5)
|
|
|
{
|
|
|
Rotation = dir.Angle();
|
|
|
- _velocity = MoveAndSlide(_velocity);
|
|
|
+ MoveAndSlide();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -412,21 +384,21 @@ And the code for the Bullet:
|
|
|
extends CharacterBody2D
|
|
|
|
|
|
var speed = 750
|
|
|
- var velocity = Vector2()
|
|
|
|
|
|
- func start(pos, dir):
|
|
|
- rotation = dir
|
|
|
- position = pos
|
|
|
+ func start(_position, _direction):
|
|
|
+ rotation = _direction
|
|
|
+ position = _position
|
|
|
velocity = Vector2(speed, 0).rotated(rotation)
|
|
|
|
|
|
func _physics_process(delta):
|
|
|
var collision = move_and_collide(velocity * delta)
|
|
|
if collision:
|
|
|
- velocity = velocity.bounce(collision.normal)
|
|
|
- if collision.collider.has_method("hit"):
|
|
|
- collision.collider.hit()
|
|
|
+ velocity = velocity.bounce(collision.get_normal())
|
|
|
+ if collision.get_collider().has_method("hit"):
|
|
|
+ collision.get_collider().hit()
|
|
|
|
|
|
func _on_VisibilityNotifier2D_screen_exited():
|
|
|
+ # Deletes the bullet when it exits the screen.
|
|
|
queue_free()
|
|
|
|
|
|
.. code-tab:: csharp
|
|
@@ -436,30 +408,30 @@ And the code for the Bullet:
|
|
|
public partial class Bullet : CharacterBody2D
|
|
|
{
|
|
|
public int Speed = 750;
|
|
|
- private Vector2 _velocity = new Vector2();
|
|
|
|
|
|
- public void Start(Vector2 pos, float dir)
|
|
|
+ public void Start(Vector2 position, float direction)
|
|
|
{
|
|
|
- Rotation = dir;
|
|
|
- Position = pos;
|
|
|
- _velocity = new Vector2(speed, 0).Rotated(Rotation);
|
|
|
+ Rotation = direction;
|
|
|
+ Position = position;
|
|
|
+ Velocity = new Vector2(speed, 0).Rotated(Rotation);
|
|
|
}
|
|
|
|
|
|
- public override void _PhysicsProcess(float delta)
|
|
|
+ public override void _PhysicsProcess(double delta)
|
|
|
{
|
|
|
- var collision = MoveAndCollide(_velocity * delta);
|
|
|
+ var collision = MoveAndCollide(Velocity * (float)delta);
|
|
|
if (collision != null)
|
|
|
{
|
|
|
- _velocity = _velocity.Bounce(collision.Normal);
|
|
|
- if (collision.Collider.HasMethod("Hit"))
|
|
|
+ Velocity = Velocity.Bounce(collision.GetNormal());
|
|
|
+ if (collision.GetCollider().HasMethod("Hit"))
|
|
|
{
|
|
|
- collision.Collider.Call("Hit");
|
|
|
+ collision.GetCollider().Call("Hit");
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
public void OnVisibilityNotifier2DScreenExited()
|
|
|
{
|
|
|
+ // Deletes the bullet when it exits the screen.
|
|
|
QueueFree();
|
|
|
}
|
|
|
}
|
|
@@ -483,10 +455,10 @@ Platformer movement
|
|
|
|
|
|
Let's try one more popular example: the 2D platformer. ``move_and_slide()``
|
|
|
is ideal for quickly getting a functional character controller up and running.
|
|
|
-If you've downloaded the sample project, you can find this in "Platformer.tscn".
|
|
|
+If you've downloaded the sample project, you can find this in "platformer.tscn".
|
|
|
|
|
|
-For this example, we'll assume you have a level made of ``StaticBody2D`` objects.
|
|
|
-They can be any shape and size. In the sample project, we're using
|
|
|
+For this example, we'll assume you have a level made of one or more ``StaticBody2D``
|
|
|
+objects. They can be any shape and size. In the sample project, we're using
|
|
|
:ref:`Polygon2D <class_Polygon2D>` to create the platform shapes.
|
|
|
|
|
|
Here's the code for the player body:
|
|
@@ -497,91 +469,68 @@ Here's the code for the player body:
|
|
|
|
|
|
extends CharacterBody2D
|
|
|
|
|
|
- export (int) var run_speed = 100
|
|
|
- export (int) var jump_speed = -400
|
|
|
- export (int) var gravity = 1200
|
|
|
+ var speed = 300.0
|
|
|
+ var jump_speed = -400.0
|
|
|
|
|
|
- var velocity = Vector2()
|
|
|
- var jumping = false
|
|
|
+ # Get the gravity from the project settings so you can sync with rigid body nodes.
|
|
|
+ var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")
|
|
|
|
|
|
- func get_input():
|
|
|
- velocity.x = 0
|
|
|
- var right = Input.is_action_pressed('ui_right')
|
|
|
- var left = Input.is_action_pressed('ui_left')
|
|
|
- var jump = Input.is_action_just_pressed('ui_select')
|
|
|
-
|
|
|
- if jump and is_on_floor():
|
|
|
- jumping = true
|
|
|
- velocity.y = jump_speed
|
|
|
- if right:
|
|
|
- velocity.x += run_speed
|
|
|
- if left:
|
|
|
- velocity.x -= run_speed
|
|
|
|
|
|
func _physics_process(delta):
|
|
|
- get_input()
|
|
|
+ # Add the gravity.
|
|
|
velocity.y += gravity * delta
|
|
|
- if jumping and is_on_floor():
|
|
|
- jumping = false
|
|
|
- velocity = move_and_slide(velocity, Vector2(0, -1))
|
|
|
+
|
|
|
+ # Handle Jump.
|
|
|
+ if Input.is_action_just_pressed("jump") and is_on_floor():
|
|
|
+ velocity.y = jump_speed
|
|
|
+
|
|
|
+ # Get the input direction.
|
|
|
+ var direction = Input.get_axis("ui_left", "ui_right")
|
|
|
+ velocity.x = direction * speed
|
|
|
+
|
|
|
+ move_and_slide()
|
|
|
|
|
|
.. code-tab:: csharp
|
|
|
|
|
|
using Godot;
|
|
|
|
|
|
- public partial class KBExample : CharacterBody2D
|
|
|
+ public partial class CBExample : CharacterBody2D
|
|
|
{
|
|
|
- [Export] public int RunSpeed = 100;
|
|
|
- [Export] public int JumpSpeed = -400;
|
|
|
- [Export] public int Gravity = 1200;
|
|
|
+ public float Speed = 100.0f;
|
|
|
+ public float JumpSpeed = -400.0f;
|
|
|
|
|
|
- Vector2 velocity = new Vector2();
|
|
|
- bool jumping = false;
|
|
|
+ // Get the gravity from the project settings so you can sync with rigid body nodes.
|
|
|
+ public float Gravity = ProjectSettings.GetSetting("physics/2d/default_gravity").AsSingle();
|
|
|
|
|
|
- public void GetInput()
|
|
|
+ public override void _PhysicsProcess(double delta)
|
|
|
{
|
|
|
- velocity.x = 0;
|
|
|
- bool right = Input.IsActionPressed("ui_right");
|
|
|
- bool left = Input.IsActionPressed("ui_left");
|
|
|
- bool jump = Input.IsActionPressed("ui_select");
|
|
|
+ Vector2 velocity = Velocity;
|
|
|
|
|
|
- if (jump && IsOnFloor())
|
|
|
- {
|
|
|
- jumping = true;
|
|
|
+ // Add the gravity.
|
|
|
+ velocity.y += Gravity * (float)delta;
|
|
|
+
|
|
|
+ // Handle jump.
|
|
|
+ if (Input.IsActionJustPressed("jump") && IsOnFloor())
|
|
|
velocity.y = JumpSpeed;
|
|
|
- }
|
|
|
|
|
|
- if (right)
|
|
|
- velocity.x += RunSpeed;
|
|
|
- if (left)
|
|
|
- velocity.x -= RunSpeed;
|
|
|
- }
|
|
|
+ // Get the input direction.
|
|
|
+ Vector2 direction = Input.GetAxis("ui_left", "ui_right");
|
|
|
+ velocity.x = direction * Speed;
|
|
|
|
|
|
- public override void _PhysicsProcess(float delta)
|
|
|
- {
|
|
|
- GetInput();
|
|
|
- velocity.y += Gravity * delta;
|
|
|
- if (jumping && IsOnFloor())
|
|
|
- jumping = false;
|
|
|
- velocity = MoveAndSlide(velocity, new Vector2(0, -1));
|
|
|
+ Velocity = velocity;
|
|
|
+ MoveAndSlide();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
.. image:: img/k2d_platform.gif
|
|
|
|
|
|
-When using ``move_and_slide()``, the function returns a vector representing the
|
|
|
-movement that remained after the slide collision occurred. Setting that value back
|
|
|
-to the character's ``velocity`` allows us to move up and down slopes smoothly. Try
|
|
|
-removing ``velocity =`` and see what happens if you don't do this.
|
|
|
-
|
|
|
-Also note that we've added ``Vector2(0, -1)`` as the floor normal. This vector points
|
|
|
-straight upward. As a result, if the character collides with an object
|
|
|
-that has this normal, it will be considered a floor.
|
|
|
-
|
|
|
-Using the floor normal allows us to make jumping work, using ``is_on_floor()``.
|
|
|
-This function will only return ``true`` after a ``move_and_slide()`` collision
|
|
|
-where the colliding body's normal is within 45 degrees of the given floor
|
|
|
-vector. You can control the maximum angle by setting ``floor_max_angle``.
|
|
|
+In this code we're using ``move_and_slide()`` as described above - to move the body
|
|
|
+along its velocity vector, sliding along any collision surfaces such as the ground
|
|
|
+or a platform. We're also using ``is_on_floor()`` to check if a jump should be
|
|
|
+allowed. Without this, you'd be able to "jump" in midair; great if you're making
|
|
|
+Flappy Bird, but not for a platformer game.
|
|
|
|
|
|
-This angle also allows you to implement other features like wall jumps using
|
|
|
-``is_on_wall()``, for example.
|
|
|
+There is a lot more that goes into a complete platformer character: acceleration,
|
|
|
+double-jumps, coyote-time, and many more. The code above is just a starting point.
|
|
|
+You can use it as a base to expand into whatever movement behavior you need for
|
|
|
+your own projects.
|