Attention: Here be dragons
This is the latest
(unstable) version of this documentation, which may document features
not available in or compatible with released stable versions of Godot.
Checking the stable version of the documentation...
Використання CharacterBody2D/3D
Вступ
Godot пропонує кілька об’єктів зіткнення, щоб забезпечити як виявлення зіткнення, так і відповідь. Спроба вирішити, який з них використовувати для вашого проекту, може заплутати. Ви зможете уникнути проблем і спростити розробку, якщо зрозумієте, як кожен з них працює і в чому його плюси і мінуси. У цьому посібнику ми розглянемо вузол CharacterBody2D і покажемо кілька прикладів його використання.
Примітка
У той час як у цьому документі використовується CharacterBody2D у своїх прикладах, ті самі поняття також застосовуються в 3D.
Що таке тіло персонажа?
CharacterBody2D призначений для реалізації тіл, які керуються через код. Тіла персонажів виявляють зіткнення з іншими тілами під час руху, але на них не впливають фізичні властивості двигуна, як-от сила тяжіння чи тертя. Хоча це означає, що вам потрібно написати певний код, щоб створити їх поведінку, це також означає, що ви маєте більш точний контроль над тим, як вони рухаються та реагують.
Despite its name CharacterBody2D, it can also be used for other physics objects that require
precise manual movement logic and detailed collision information, such as moving
platforms or complex projectiles.
Примітка
Цей документ передбачає, що ви знайомі з різними фізичними тілами Godot. Будь ласка, спочатку прочитайте Вступ до курсу фізики для огляду варіантів фізики.
Порада
На CharacterBody2D може впливати сила тяжіння та інші сили, але ви повинні обчислити рух у коді. Фізичний механізм не переміщуватиме CharacterBody2D.
Рух і зіткнення
Переміщуючи CharacterBody2D, ви не повинні встановлювати його властивість position безпосередньо. Замість цього ви використовуєте методи move_and_collide() або move_and_slide(). Ці методи переміщують тіло по заданому вектору та виявляють зіткнення.
Попередження
Ви повинні обробляти фізичний рух тіла у зворотному виклику _physics_process().
Два методи руху служать різним цілям, і далі в цьому посібнику ви побачите приклади їх роботи.
move_and_collide
Цей метод приймає один обов’язковий параметр: a Vector2, що вказує на відносний рух тіла. Як правило, це ваш вектор швидкості, помножений на часовий крок кадру (дельта). Якщо двигун виявить зіткнення в будь-якому місці цього вектора, тіло негайно припинить рух. Якщо це станеться, метод поверне об’єкт KinematicCollision2D.
KinematicCollision2D — це об’єкт, що містить дані про зіткнення та об’єкт зіткнення. Використовуючи ці дані, ви можете розрахувати реакцію на зіткнення.
move_and_collide найбільш корисний, коли ви просто хочете перемістити тіло та виявити зіткнення, але не потребуєте автоматичної реакції на зіткнення. Наприклад, якщо вам потрібна куля, яка рикошетить від стіни, ви можете безпосередньо змінити кут швидкості, коли виявите зіткнення. Дивіться приклад нижче.
move_and_slide
Метод move_and_slide() призначений для спрощення реакції на зіткнення у типовому випадку, коли ви хочете, щоб одне тіло ковзало вздовж іншого. Це особливо корисно, наприклад, у платформерах або іграх з видом зверху.
Під час виклику move_and_slide() функція використовує низку властивостей вузла для обчислення його поведінки ковзання. Ці властивості можна знайти в інспекторі або встановити в коді.
velocity- значення за замовчуванням:Vector2( 0, 0 )Ця властивість представляє вектор швидкості тіла в пікселях на секунду.
move_and_slide()автоматично змінить це значення під час зіткнення.motion_mode- значення за замовчуванням:MOTION_MODE_GROUNDEDЦя властивість зазвичай використовується, щоб розрізнити бічне прокручування та рух зверху вниз. Використовуючи значення за замовчуванням, ви можете використовувати методи
is_on_floor(),is_on_wall()іis_on_ceiling(), щоб визначити, з яким типом поверхні контактує тіло, і тіло буде взаємодіяти зі схилами. При використанніMOTION_MODE_FLOATINGусі зіткнення вважатимуться "стінами".up_direction- значення за замовчуванням:Вектор2( 0, -1 )Ця властивість дозволяє визначити, які поверхні двигун повинен вважати підлогою. Його значення дозволяє використовувати методи
is_on_floor(),is_on_wall()іis_on_ceiling(), щоб визначити, з яким типом поверхні контактує тіло. Значення за замовчуванням означає, що верхня сторона горизонтальних поверхонь вважатиметься «грунтом».floor_stop_on_slope- значення за замовчуванням:trueЦей параметр запобігає ковзанню тіла вниз по схилах під час стояння.
wall_min_slide_angle- значення за замовчуванням:0,261799(у радіанах, еквівалентно15градусам)Це мінімальний кут, під яким тіло може ковзати при наїзді на схил.
floor_max_angle- значення за замовчуванням:0,785398(у радіанах, еквівалентно45градусам)Цей параметр є максимальним кутом перед тим, як поверхня більше не вважатиметься «підлогою»
Є багато інших властивостей, які можна використовувати для зміни поведінки організму за певних обставин. Перегляньте документацію CharacterBody2D для повної інформації.
Виявлення зіткнень
При використанні move_and_collide() функція повертає KinematicCollision2D безпосередньо, і ви можете використовувати це у своєму коді.
Під час використання move_and_slide() можливе виникнення кількох зіткнень, оскільки обчислюється відповідь на слайд. Щоб обробити ці зіткнення, використовуйте get_slide_collision_count() і get_slide_collision():
# Using move_and_collide.
var collision = move_and_collide(velocity * delta)
if collision:
print("I collided with ", collision.get_collider().name)
# Using move_and_slide.
move_and_slide()
for i in get_slide_collision_count():
var collision = get_slide_collision(i)
print("I collided with ", collision.get_collider().name)
// Using MoveAndCollide.
var collision = MoveAndCollide(Velocity * (float)delta);
if (collision != null)
{
GD.Print("I collided with ", ((Node)collision.GetCollider()).Name);
}
// Using MoveAndSlide.
MoveAndSlide();
for (int i = 0; i < GetSlideCollisionCount(); i++)
{
var collision = GetSlideCollision(i);
GD.Print("I collided with ", ((Node)collision.GetCollider()).Name);
}
Примітка
get_slide_collision_count() підраховує лише рази, коли тіло зіткнулося та змінило напрямок.
Перегляньте KinematicCollision2D, щоб дізнатися, які дані про зіткнення повертаються.
Який метод руху використовувати?
Поширене запитання нових користувачів Godot: "Як ви вирішуєте, яку функцію руху використовувати?" Часто у відповідь використовують move_and_slide(), оскільки це здається простішим, але це не обов’язково так. Один із способів подумати про це так: move_and_slide() є окремим випадком, а move_and_collide() є більш загальним. Наприклад, наступні два фрагменти коду призводять до однакової відповіді на конфлікт:
# using move_and_collide
var collision = move_and_collide(velocity * delta)
if collision:
velocity = velocity.slide(collision.get_normal())
# using move_and_slide
move_and_slide()
// using MoveAndCollide
var collision = MoveAndCollide(Velocity * (float)delta);
if (collision != null)
{
Velocity = Velocity.Slide(collision.GetNormal());
}
// using MoveAndSlide
MoveAndSlide();
Усе, що ви робите за допомогою move_and_slide(), також можна зробити за допомогою move_and_collide(), але для цього може знадобитися трохи більше коду. Однак, як ми побачимо в наведених нижче прикладах, є випадки, коли move_and_slide() не надає потрібної відповіді.
У наведеному вище прикладі move_and_slide() автоматично змінює змінну velocity. Це пояснюється тим, що коли персонаж стикається з навколишнім середовищем, функція внутрішньо перераховує швидкість, щоб відобразити уповільнення.
Наприклад, якщо ваш персонаж впав на підлогу, ви не хочете, щоб він накопичував вертикальну швидкість через дію сили тяжіння. Натомість ви хочете скинути його вертикальну швидкість до нуля.
move_and_slide() також може перераховувати кінематичну швидкість тіла кілька разів у циклі, оскільки для створення плавного руху воно переміщує персонажа та стикається до п’яти разів за замовчуванням. Наприкінці процесу нова швидкість персонажа доступна для використання в наступному кадрі.
Приклади
Щоб побачити ці приклади в дії, завантажте приклад проекту: character_body_2d_starter.zip
Рух і стіни
Якщо ви завантажили зразок проекту, цей приклад знаходиться в "basic_movement.tscn".
Для цього прикладу додайте CharacterBody2D з двома дочірніми елементами: Sprite2D і CollisionShape2D. Використовуйте Godot "icon.svg" як текстуру Sprite2D (перетягніть його з док-станції Filesystem до властивості Texture Sprite2D). У властивості Shape CollisionShape2D виберіть «New RectangleShape2D» і задайте розмір прямокутника так, щоб він поміщався над зображенням спрайту.
Примітка
Дивіться Огляд руху в 2D просторі для прикладів реалізації двовимірних схем руху.
Додайте скрипт до CharacterBody2D і додайте такий код:
extends CharacterBody2D
var speed = 300
func get_input():
var input_dir = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
velocity = input_dir * speed
func _physics_process(delta):
get_input()
move_and_collide(velocity * delta)
using Godot;
public partial class MyCharacterBody2D : CharacterBody2D
{
private int _speed = 300;
public void GetInput()
{
Vector2 inputDir = Input.GetVector("ui_left", "ui_right", "ui_up", "ui_down");
Velocity = inputDir * _speed;
}
public override void _PhysicsProcess(double delta)
{
GetInput();
MoveAndCollide(Velocity * (float)delta);
}
}
Запустіть цю сцену, і ви побачите, що move_and_collide() працює належним чином, переміщуючи тіло вздовж вектора швидкості. Тепер давайте подивимося, що станеться, якщо ви додасте кілька перешкод. Додайте StaticBody2D з прямокутною формою зіткнення. Для видимості можна використовувати Sprite2D, Polygon2D або ввімкнути «Видимі форми зіткнень» у меню «Налагодження».
Запустіть сцену ще раз і спробуйте натрапити на перешкоду. Ви побачите, що CharacterBody2D не може подолати перешкоду. Однак спробуйте наблизитися до перешкоди під кутом, і ви побачите, що перешкода діє як клей – таке відчуття, ніби тіло застрягло.
Це відбувається тому, що немає відповіді на зіткнення. move_and_collide() зупиняє рух тіла, коли відбувається зіткнення. Нам потрібно закодувати будь-яку відповідь від зіткнення, яку ми хочемо.
Спробуйте змінити функцію на move_and_slide() і запустіть знову.
move_and_slide() забезпечує реакцію на зіткнення за замовчуванням ковзанням тіла вздовж об’єкта зіткнення. Це корисно для багатьох типів ігор і може бути всім, що вам потрібно, щоб отримати бажану поведінку.
Підстрибуючи/відображаючи
Що робити, якщо вам не потрібна ковзаюча реакція на зіткнення? У цьому прикладі ("bounce_and_collide.tscn" у прикладі проекту) у нас є персонаж, який стріляє кулями, і ми хочемо, щоб кулі відскакували від стін.
У цьому прикладі використовуються три сцени. Основна сцена містить гравця та стіни. Куля та Стіна є окремими сценами, тому їх можна інстансувати.
Гравець керується клавішами w і s для руху вперед і назад. Для прицілювання використовується вказівник миші. Ось код для програвача, який використовує move_and_slide():
extends CharacterBody2D
var Bullet = preload("res://bullet.tscn")
var speed = 200
func get_input():
# Add these actions in Project Settings -> Input Map.
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_tree().root.add_child(b)
func _physics_process(delta):
get_input()
var dir = get_global_mouse_position() - global_position
# Don't move if too close to the mouse pointer.
if dir.length() > 5:
rotation = dir.angle()
move_and_slide()
using Godot;
public partial class MyCharacterBody2D : CharacterBody2D
{
private PackedScene _bullet = GD.Load<PackedScene>("res://Bullet.tscn");
private int _speed = 200;
public void GetInput()
{
// Add these actions in Project Settings -> Input Map.
float inputDir = Input.GetAxis("backward", "forward");
Velocity = Transform.X * inputDir * _speed;
if (Input.IsActionPressed("shoot"))
{
Shoot();
}
}
public void Shoot()
{
// "Muzzle" is a Marker2D placed at the barrel of the gun.
var b = (Bullet)_bullet.Instantiate();
b.Start(GetNode<Node2D>("Muzzle").GlobalPosition, Rotation);
GetTree().Root.AddChild(b);
}
public override void _PhysicsProcess(double delta)
{
GetInput();
var dir = GetGlobalMousePosition() - GlobalPosition;
// Don't move if too close to the mouse pointer.
if (dir.Length() > 5)
{
Rotation = dir.Angle();
MoveAndSlide();
}
}
}
І код для Bullet:
extends CharacterBody2D
var speed = 750
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.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()
using Godot;
public partial class Bullet : CharacterBody2D
{
public int _speed = 750;
public void Start(Vector2 position, float direction)
{
Rotation = direction;
Position = position;
Velocity = new Vector2(speed, 0).Rotated(Rotation);
}
public override void _PhysicsProcess(double delta)
{
var collision = MoveAndCollide(Velocity * (float)delta);
if (collision != null)
{
Velocity = Velocity.Bounce(collision.GetNormal());
if (collision.GetCollider().HasMethod("Hit"))
{
collision.GetCollider().Call("Hit");
}
}
}
private void OnVisibilityNotifier2DScreenExited()
{
// Deletes the bullet when it exits the screen.
QueueFree();
}
}
Дія відбувається в _physics_process(). Після використання move_and_collide(), якщо відбувається зіткнення, повертається об’єкт KinematicCollision2D (в іншому випадку повертається null).
Якщо є зворотне зіткнення, ми використовуємо нормаль зіткнення, щоб відобразити швидкість кулі за допомогою методу Vector2.bounce().
Якщо об’єкт зіткнення (collider) має метод hit, ми також викликаємо його. У прикладі проекту ми додали ефект миготливого кольору до стіни, щоб продемонструвати це.
Рух платформер
Давайте спробуємо ще один популярний приклад: 2D-платформер. move_and_slide() ідеально підходить для швидкого запуску та запуску функціонального контролера символів. Якщо ви завантажили зразок проекту, ви можете знайти його в "platformer.tscn".
Для цього прикладу ми припустимо, що у вас є рівень, який складається з одного або кількох об’єктів StaticBody2D. Вони можуть бути будь-якої форми і розміру. У прикладі проекту ми використовуємо Polygon2D для створення форм платформи.
Ось код для тіла гравця:
extends CharacterBody2D
var speed = 300.0
var jump_speed = -400.0
func _physics_process(delta):
# Add the gravity.
velocity.y += get_gravity() * delta
# 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()
using Godot;
public partial class MyCharacterBody2D : CharacterBody2D
{
private float _speed = 100.0f;
private float _jumpSpeed = -400.0f;
// Get the gravity from the project settings so you can sync with rigid body nodes.
public override void _PhysicsProcess(double delta)
{
Vector2 velocity = Velocity;
// Add the gravity.
velocity.Y += GetGravity() * (float)delta;
// Handle jump.
if (Input.IsActionJustPressed("jump") && IsOnFloor())
{
velocity.Y = _jumpSpeed;
}
// Get the input direction.
float direction = Input.GetAxis("ui_left", "ui_right");
velocity.X = direction * _speed;
Velocity = velocity;
MoveAndSlide();
}
}
У цьому коді ми використовуємо move_and_slide(), як описано вище - для переміщення тіла вздовж його вектора швидкості, ковзаючи вздовж будь-яких поверхонь зіткнення, таких як земля або платформа. Ми також використовуємо is_on_floor(), щоб перевірити, чи можна дозволити стрибок. Без цього ви могли б «стрибнути» в повітрі; чудово, якщо ви створюєте Flappy Bird, але не для платформерної гри.
Існує багато іншого, що входить у повний персонаж платформера: прискорення, подвійні стрибки, час койота та багато іншого. Наведений вище код є лише відправною точкою. Ви можете використовувати його як основу для розширення будь-якої поведінки руху, яка вам потрібна для ваших власних проектів.