Character - Персонаж
Передумови
Підготовка до створення персонажа.
Node Структура
Структура Character
По суті своїй Character/Player/Персонаж/Гравець - це не є один Node, а їх колекція.
Разом ці Node-и створюють структуру/об'єкт/Scene.
Кожен з цих Node-ів виконують якусь певну функцію, наприклад: Колізія, Спрайт, Камера та ін.
Сама проста структура персонажа виглядає так
- Player
- CharacterBody2d
- Camera2D
- CollisionShape2D
- AnimatedSprite2D
Настройка Node-ів
Налаштування Node-ів нашого персонажа.
Переміщення групи Node-ів та настройка камери:
Для початку нам потрібно перемістити нашого персонажа:
Настройте камеру:
Настройка AnimatedSprite2D:
Створіть Sprite Frame та додайте кадри для анімації:
Змініть розмір нашого персонажа на доречний для вашої карти:
Змініть настройки рендерингу проєкту для того, щоб ваш персонаж коректно малювався:
Настройки анімації:
Змініть назву анімації на idle (idle - очікування, відсутність дій):
Додайте walk анімацію:
Зробіть так щоб анімація idle запускалась автоматично:
Настройка CollisionShape2D:
Зробіть так щоб CollisionShape2D персонажа повністю охоплював його структуру:
Додайте базовий код персонажа:
using Godot;
public partial class CharacterBody2d : CharacterBody2D
{
// Швидкість руху персонажа. Використовуємо float, бо це число з плаваючою комою, що дозволяє більш плавне керування.
private const float Speed = 500.0f;
// Метод, який викликається, коли вузол (node) стає готовим до роботи.
// Це схоже на метод `main` в інших мовах. Тут ми можемо виконати початкову ініціалізацію.
public override void _Ready()
{
// Виводимо текст "TEST!" в консоль Godot для перевірки, що скрипт працює.
GD.Print("TEST!");
}
// Метод, який викликається на кожному фізичному кадрі.
// Фізичний кадр відбувається з фіксованою частотою, що корисно для фізичних симуляцій.
// double delta - це час, який минув між поточним та попереднім фізичним кадром.
public override void _PhysicsProcess(double delta)
{
// Отримуємо напрямок руху від користувача.
Vector2 direction = GetDirection();
// Обчислюємо вектор швидкості, помножуючи напрямок на швидкість.
Vector2 velocity = direction * Speed;
// Встановлюємо швидкість персонажа. CharacterBody2D має властивість Velocity, яка визначає його швидкість.
Velocity = velocity;
// Переміщуємо персонажа і обробляємо колізії.
// MoveAndSlide() використовує фізичний рушій Godot для переміщення персонажа і обробки зіткнень з іншими об'єктами.
MoveAndSlide();
}
// Метод, який визначає напрямок руху персонажа на основі натиснутих клавіш.
public Vector2 GetDirection()
{
// Створюємо новий вектор напрямку, початково встановлений в (0, 0).
Vector2 direction = new Vector2();
// Перевіряємо, чи натиснута клавіша "W". Якщо так, додаємо -1 до Y-компоненти напрямку (рух вгору).
if (Input.IsPhysicalKeyPressed(Key.W))
{
direction.Y += -1;
}
// Перевіряємо, чи натиснута клавіша "S". Якщо так, додаємо 1 до Y-компоненти напрямку (рух вниз).
if (Input.IsPhysicalKeyPressed(Key.S))
{
direction.Y += 1;
}
// Перевіряємо, чи натиснута клавіша "A". Якщо так, додаємо -1 до X-компоненти напрямку (рух вліво).
if (Input.IsPhysicalKeyPressed(Key.A))
{
direction.X += -1;
}
// Перевіряємо, чи натиснута клавіша "D". Якщо так, додаємо 1 до X-компоненти напрямку (рух вправо).
if (Input.IsPhysicalKeyPressed(Key.D))
{
direction.X += 1;
}
// Повертаємо вектор напрямку.
return direction;
}
}Покращення коду персонажа - Теорія
Як покращити код нашого персонажа? - Теорія
Для початку потрібно визначити основні проблеми базового коду поданого раніше, а саме:
- Відсутність інерції.
- Відсутність гравітації.
- Спрайт не реагує на напрямок руху персонажа.
- Анімація спрайта також не реагує на стан персонажа.
1 - Відсутність інерції
Інерція — явище збереження швидкості руху матеріальної точки за відсутності зовнішніх сил.
Зараз наш персонаж не має інерції, тобто - якщо ви відпускаєте клавішу він зупиняє свій рух моментально.
Причина у тому, що кожен кадр (фізичний) ми вираховуємо швидкість персонажа заново:
Vector2 velocity = direction * Speed;Це проблема, тому що наш персонаж 'забуває' свою швидкість з минулого кадру.
Щоб це виправити нам потрібно змінювати, а не перезаписувати його швидкість:
public override void _PhysicsProcess(double delta) // Перевизначаємо метод _PhysicsProcess(), який викликається з фіксованою частотою, щоб виконувати фізичні розрахунки. `delta` - час, що минув з попереднього виклику.
{
Vector2 direction = GetDirection(); // Отримуємо вектор напрямку руху від методу GetDirection().
Vector2 velocity = Velocity; // Отримуємо поточну швидкість.
velocity += direction * Speed * (float)delta; // Обчислюємо нову швидкість, додаючи до поточної швидкості вектор напрямку, помножений на швидкість та дельту. Важливо перетворити `delta` на `float` для правильних обчислень.
Velocity = velocity; // Присвоюємо обчислену швидкість властивості Velocity.
MoveAndSlide(); // Переміщуємо персонажа і обробляємо колізії.
}У методі _PhysicsProcess параметр delta — це час, який минув між поточним і попереднім кадром (в секундах). Він гарантує, що рух буде однаковим незалежно від частоти кадрів. Наприклад, якщо ваш комп'ютер працює з 60 кадрів на секунду, то delta буде приблизно 0.0167 секунди. Якщо інший комп'ютер працює з 30 кадрів на секунду — delta буде більшим, але рух все одно буде виглядати однаково.
Як delta впливає на рух:
У коді ми множимо швидкість на delta, щоб рух був плавним і однаковим. Якби ми не враховували delta, то рух був би швидшим на швидших комп'ютерах і повільнішим на повільних.
Інерція:
Тому, що ми спочатку копіюємо поточну швидкість (Velocity) у змінну velocity, а потім додаємо нове значення, ми отримуємо плавне прискорення. Це створює відчуття інерції — коли ви натискаєте клавішу, об'єкт не миттєво змінює швидкість, а поступово набирає її.
1.2 - інерція - проблема.
В принципі, зараз інерція працює правильно, але наш персонаж ніколи не зупиниться, ніби він у вакуумі, та на нього не діє гравітація.
Щоб це виправити нам потрібно додати супротив:
public override void _PhysicsProcess(double delta) // Перевизначаємо метод _PhysicsProcess(), який викликається з фіксованою частотою, щоб виконувати фізичні розрахунки. `delta` - час, що минув з попереднього виклику.
{
Vector2 direction = GetDirection(); // Отримуємо вектор напрямку руху від методу GetDirection().
Vector2 velocity = Velocity; // Отримуємо поточну швидкість.
velocity += direction * Speed * (float)delta; // Обчислюємо нову швидкість, додаючи до поточної швидкості вектор напрямку, помножений на швидкість та дельту. Важливо перетворити `delta` на `float` для правильних обчислень.
velocity *= 0.98f; // Добавляємо супротив, зараз персонаж буде з часом зупинятись.
Velocity = velocity; // Присвоюємо обчислену швидкість властивості Velocity.
MoveAndSlide(); // Переміщуємо персонажа та обробляємо колізії.
}using Godot;
public partial class CharacterBody2d : CharacterBody2D
{
// Швидкість персонажа
private const float Speed = 500.0f;
public override void _Ready()
{
// Виведення тестового повідомлення в консоль
GD.Print("ТЕСТ!");
}
public override void _PhysicsProcess(double delta)
{
// Отримання напрямку руху з методу GetDirection
Vector2 direction = GetDirection();
// Отримання поточного вектора швидкості
Vector2 velocity = Velocity;
// Додавання прискорення в напрямку руху
velocity += direction * Speed * (float)delta;
// Додаткове затухання швидкості для реалістичного руху
velocity *= 0.98f;
// Застосування нової швидкості до персонажа
Velocity = velocity;
// Виконання руху з урахуванням колізій
MoveAndSlide();
}
// Метод для визначення напрямку руху за клавішами
public Vector2 GetDirection()
{
// Створення вектора напрямку
Vector2 direction = new Vector2();
// Перевірка натискання клавіші W (вгору)
if (Input.IsPhysicalKeyPressed(Key.W))
{
direction.Y += -1;
}
// Перевірка натискання клавіші S (вниз)
if (Input.IsPhysicalKeyPressed(Key.S))
{
direction.Y += 1;
}
// Перевірка натискання клавіші A (вліво)
if (Input.IsPhysicalKeyPressed(Key.A))
{
direction.X += -1;
}
// Перевірка натискання клавіші D (вправо)
if (Input.IsPhysicalKeyPressed(Key.D))
{
direction.X += 1;
}
// Повернення вектора напрямку
return direction;
}
}2 - Відсутність гравітації.
Гравітації або тяжіння — властивість тіл із масою притягуватись одне до одного. (у нашому випадку персонаж та 'підлога')
Наш персонаж (людина), не має літати, а має ходити по землі або падати.
Для цього нам потрібна гравітації - сила, яка буде тягнути нашого персонажа до землі.
Для цього ми створимо новий Vector2D - Gravity з напрямком вниз:
private Vector2 Gravity = new Vector2(0, 50f);А тепер застосуємо її до нашого персонажа:
velocity += Gravity;Вся функція _PhysicsProcess повинна виглядати так:
public override void _PhysicsProcess(double delta) // Перевизначаємо метод _PhysicsProcess(), який викликається з фіксованою частотою, щоб виконувати фізичні розрахунки. `delta` - час, що минув з попереднього виклику.
{
Vector2 direction = GetDirection(); // Отримуємо вектор напрямку руху від методу GetDirection().
Vector2 velocity = Velocity; // Отримуємо поточну швидкість.
velocity += direction * Speed * (float)delta; // Обчислюємо нову швидкість, додаючи до поточної швидкості вектор напрямку, помножений на швидкість та дельту. Важливо перетворити `delta` на `float` для правильних обчислень.
velocity += Gravity; // Додаємо гравітацію.
Velocity = velocity; // Присвоюємо обчислену швидкість властивості Velocity.
MoveAndSlide(); // Переміщуємо персонажа та обробляємо колізії.
}3 - Спрайт не реагує на напрямок руху персонажа.
Вернемось до цього у наступній темі.
4 - Анімація спрайта також не реагує на стан персонажа.
Вернемось до цього у наступній темі.
build info
- compiler
- 2.0.5.1 · 4054EF9BF6C4510A
- built
- 2026-11-08 14:31:25 UTC
WebUniverse