123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 |
- .. _doc_kinematic_character_2d:
- Kinematic character (2D)
- ========================
- Introduction
- ~~~~~~~~~~~~
- Yes, the name sounds strange. "Kinematic Character". What is that?
- The reason for the name is that, when physics engines came out, they were called
- "Dynamics" engines (because they dealt mainly with collision
- responses). Many attempts were made to create a character controller
- using the dynamics engines, but it wasn't as easy as it seemed. Godot
- has one of the best implementations of dynamic character controller
- you can find (as it can be seen in the 2d/platformer demo), but using
- it requires a considerable level of skill and understanding of
- physics engines (or a lot of patience with trial and error).
- Some physics engines, such as Havok seem to swear by dynamic character
- controllers as the best option, while others (PhysX) would rather
- promote the kinematic one.
- So, what is the difference?:
- - A **dynamic character controller** uses a rigid body with an infinite
- inertia tensor. It's a rigid body that can't rotate.
- Physics engines always let objects move and collide, then solve their
- collisions all together. This makes dynamic character controllers
- able to interact with other physics objects seamlessly, as seen in
- the platformer demo. However, these interactions are not always
- predictable. Collisions can take more than one frame to be
- solved, so a few collisions may seem to displace a tiny bit. Those
- problems can be fixed, but require a certain amount of skill.
- - A **kinematic character controller** is assumed to always begin in a
- non-colliding state, and will always move to a non-colliding state.
- If it starts in a colliding state, it will try to free itself like
- rigid bodies do, but this is the exception, not the rule. This makes
- their control and motion a lot more predictable and easier to
- program. However, as a downside, they can't directly interact with
- other physics objects, unless done by hand in code.
- This short tutorial will focus on the kinematic character controller.
- Basically, the old-school way of handling collisions (which is not
- necessarily simpler under the hood, but well hidden and presented as a
- nice and simple API).
- Physics process
- ~~~~~~~~~~~~~~~
- To manage the logic of a kinematic body or character, it is always
- advised to use physics process, because it's called before physics step and its execution is
- in sync with physics server, also it is called the same amount of times
- per second, always. This makes physics and motion calculation work in a
- more predictable way than using regular process, which might have spikes
- or lose precision if the frame rate is too high or too low.
- .. tabs::
- .. code-tab:: gdscript GDScript
- extends KinematicBody2D
- func _physics_process(delta):
- pass
- .. code-tab:: csharp
- using Godot;
- using System;
- public class PhysicsScript : KinematicBody2D
- {
- public override void _PhysicsProcess(float delta)
- {
- }
- }
- Scene setup
- ~~~~~~~~~~~
- To have something to test, here's the scene (from the tilemap tutorial):
- :download:`kbscene.zip <files/kbscene.zip>`. We'll be creating a new scene
- for the character. Use the robot sprite and create a scene like this:
- .. image:: img/kbscene.png
- You'll notice that there's a warning icon next to our CollisionShape2D node;
- that's because we haven't defined a shape for it. Create a new CircleShape2D
- in the shape property of CollisionShape2D. Click on <CircleShape2D> to go to the
- options for it, and set the radius to 30:
- .. image:: img/kbradius.png
- **Note: As mentioned before in the physics tutorial, the physics engine
- can't handle scale on most types of shapes (only collision polygons,
- planes and segments work), so always change the parameters (such as
- radius) of the shape instead of scaling it. The same is also true for
- the kinematic/rigid/static bodies themselves, as their scale affects the
- shape scale.**
- Now, create a script for the character, the one used as an example
- above should work as a base.
- Finally, instance that character scene in the tilemap, and make the
- map scene the main one, so it runs when pressing play.
- .. image:: img/kbinstance.png
- Moving the kinematic character
- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- Go back to the character scene, and open the script, the magic begins
- now! Kinematic body will do nothing by default, but it has a
- useful function called
- :ref:`KinematicBody2D.move_and_collide() <class_KinematicBody2D_method_move_and_collide>`.
- This function takes a :ref:`Vector2 <class_Vector2>` as
- an argument, and tries to apply that motion to the kinematic body. If a
- collision happens, it stops right at the moment of the collision.
- So, let's move our sprite downwards until it hits the floor:
- .. tabs::
- .. code-tab:: gdscript GDScript
- extends KinematicBody2D
- func _physics_process(delta):
- move_and_collide(Vector2(0, 1)) # Move down 1 pixel per physics frame
- .. code-tab:: csharp
- using Godot;
- using System;
- public class PhysicsScript : KinematicBody2D
- {
- public override void _PhysicsProcess(float delta)
- {
- // Move down 1 pixel per physics frame
- MoveAndCollide(new Vector2(0, 1));
- }
- }
- The result is that the character will move, but stop right when
- hitting the floor. Pretty cool, huh?
- The next step will be adding gravity to the mix, this way it behaves a
- little more like a regular game character:
- .. tabs::
- .. code-tab:: gdscript GDScript
- extends KinematicBody2D
- const GRAVITY = 200.0
- var velocity = Vector2()
- func _physics_process(delta):
- velocity.y += delta * GRAVITY
- var motion = velocity * delta
- move_and_collide(motion)
- .. code-tab:: csharp
- using Godot;
- using System;
- public class PhysicsScript : KinematicBody2D
- {
- const float gravity = 200.0f;
- Vector2 velocity;
- public override void _PhysicsProcess(float delta)
- {
- velocity.y += delta * gravity;
- var motion = velocity * delta;
- MoveAndCollide(motion);
- }
- }
- Now the character falls smoothly. Let's make it walk to the sides, left
- and right when touching the directional keys. Remember that the values
- being used (for speed at least) are pixels/second.
- This adds simple walking support by pressing left and right:
- .. tabs::
- .. code-tab:: gdscript GDScript
- extends KinematicBody2D
- const GRAVITY = 200.0
- const WALK_SPEED = 200
- var velocity = Vector2()
- func _physics_process(delta):
- velocity.y += delta * GRAVITY
- if Input.is_action_pressed("ui_left"):
- velocity.x = -WALK_SPEED
- elif Input.is_action_pressed("ui_right"):
- velocity.x = WALK_SPEED
- else:
- velocity.x = 0
- # We don't need to multiply velocity by delta because "move_and_slide" already takes delta time into account.
- # The second parameter of "move_and_slide" is the normal pointing up.
- # In the case of a 2D platformer, in Godot, upward is negative y, which translates to -1 as a normal.
- move_and_slide(velocity, Vector2(0, -1))
- .. code-tab:: csharp
- using Godot;
- using System;
- public class PhysicsScript : KinematicBody2D
- {
- const float gravity = 200.0f;
- const int walkSpeed = 200;
- Vector2 velocity;
- public override void _PhysicsProcess(float delta)
- {
- velocity.y += delta * gravity;
- if (Input.IsActionPressed("ui_left"))
- {
- velocity.x = -walkSpeed;
- }
- else if (Input.IsActionPressed("ui_right"))
- {
- velocity.x = walkSpeed;
- }
- else
- {
- velocity.x = 0;
- }
- // We don't need to multiply velocity by delta because "MoveAndSlide" already takes delta time into account.
- // The second parameter of "MoveAndSlide" is the normal pointing up.
- // In the case of a 2D platformer, in Godot, upward is negative y, which translates to -1 as a normal.
- MoveAndSlide(velocity, new Vector2(0, -1));
- }
- }
- And give it a try.
- This is a good starting point for a platformer. A more complete demo can be found in the demo zip distributed with the
- engine, or in the
- https://github.com/godotengine/godot-demo-projects/tree/master/2d/kinematic_character.
|