Character - Персонаж - 2
Передумови
Підготовка до створення персонажа.
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;
using Godot;
using System;
public partial class CharacterBody2d : CharacterBody2D
{
// Константа, що визначає швидкість переміщення персонажа.
private const float Speed = 500.0f;
// Вектор, що представляє силу гравітації, що діє на персонажа.
private Vector2 Gravity = new Vector2(0, 50f);
// Метод, що викликається Godot після того, як вузол (node) повністю готовий та доданий до дерева.
public override void _Ready()
{
// Виводимо "TEST!" в консоль для підтвердження, що вузол був ініціалізований.
GD.Print("TEST!");
}
// Метод, що викликається Godot в кожному фізичному кадрі. Використовується для обробки фізики та переміщення.
public override void _PhysicsProcess(double delta)
{
// Отримуємо вектор напрямку руху на основі вводу користувача.
Vector2 direction = GetDirection();
// Отримуємо поточну швидкість персонажа.
Vector2 velocity = Velocity;
// Додаємо до швидкості вектор напрямку, помножений на швидкість та час між кадрами (delta).
velocity += direction * Speed * (float)delta;
// Додаємо до швидкості силу гравітації.
velocity += Gravity;
// Перевіряємо, чи напрямок руху по осі X відрізняється від знаку поточної швидкості по осі X.
// Це дозволяє зменшити швидкість, коли користувач намагається рухатися в протилежному напрямку.
if (direction.X == 1 && velocity.X < 0 || direction.X == -1 && velocity.X > 0)
{
// Зменшуємо швидкість по осі X на 10%.
velocity.X *= 0.90f;
}
// Перевіряємо, чи немає жодного напрямку руху по осі X (користувач не натискає A або D).
else if (direction.X == 0)
{
// Зменшуємо швидкість по осі X на 10%, щоб зупинити персонажа.
velocity.X *= 0.90f;
}
// Виводимо значення напрямку руху по осі X в консоль для налагодження.
GD.Print(direction.X);
// Виводимо значення швидкості по осі X в консоль для налагодження.
GD.Print(velocity.X);
// Застосовуємо нову швидкість до CharacterBody2D.
Velocity = velocity;
// Викликаємо метод MoveAndSlide(), який виконує переміщення персонажа та обробляє колізії.
MoveAndSlide();
}
// Метод, що повертає вектор напрямку руху на основі натиснутих клавіш.
public Vector2 GetDirection()
{
// Ініціалізуємо вектор напрямку.
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;
}
}Код персонажа
Трішки покращений код персонажа з минулої теми.
using Godot;
using Godot;
using System;
public partial class CharacterBody2d : CharacterBody2D
{
// Константа, що визначає швидкість переміщення персонажа.
private const float Speed = 500.0f;
// Вектор, що представляє силу гравітації, що діє на персонажа.
private Vector2 Gravity = new Vector2(0, 50f);
// Метод, що викликається Godot після того, як вузол (node) повністю готовий та доданий до дерева.
public override void _Ready()
{
// Виводимо "TEST!" в консоль для підтвердження, що вузол був ініціалізований.
GD.Print("TEST!");
}
// Метод, що викликається Godot в кожному фізичному кадрі. Використовується для обробки фізики та переміщення.
public override void _PhysicsProcess(double delta)
{
// Отримуємо вектор напрямку руху на основі вводу користувача.
Vector2 direction = GetDirection();
// Отримуємо поточну швидкість персонажа.
Vector2 velocity = Velocity;
// Додаємо до швидкості вектор напрямку, помножений на швидкість та час між кадрами (delta).
velocity += direction * Speed * (float)delta;
// Додаємо до швидкості силу гравітації.
velocity += Gravity;
// Перевіряємо, чи напрямок руху по осі X відрізняється від знаку поточної швидкості по осі X.
// Це дозволяє зменшити швидкість, коли користувач намагається рухатися в протилежному напрямку.
if (direction.X == 1 && velocity.X < 0 || direction.X == -1 && velocity.X > 0)
{
// Зменшуємо швидкість по осі X на 10%.
velocity.X *= 0.90f;
}
// Перевіряємо, чи немає жодного напрямку руху по осі X (користувач не натискає A або D).
else if (direction.X == 0)
{
// Зменшуємо швидкість по осі X на 10%, щоб зупинити персонажа.
velocity.X *= 0.90f;
}
// Виводимо значення напрямку руху по осі X в консоль для налагодження.
GD.Print(direction.X);
// Виводимо значення швидкості по осі X в консоль для налагодження.
GD.Print(velocity.X);
// Застосовуємо нову швидкість до CharacterBody2D.
Velocity = velocity;
// Викликаємо метод MoveAndSlide(), який виконує переміщення персонажа та обробляє колізії.
MoveAndSlide();
}
// Метод, що повертає вектор напрямку руху на основі натиснутих клавіш.
public Vector2 GetDirection()
{
// Ініціалізуємо вектор напрямку.
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 - Відсутність інерції
Обговорено у минулій темі.
2 - Відсутність гравітації.
Обговорено у минулій темі.
3 - Спрайт не реагує на напрямок руху персонажа.
Коли наш персонаж міняє напрям руху його спрайт повинен це відображати.
На цей момент спрайт персонажа завжди дивиться в одну сторону.
Нам потрібно щоб наш персонаж 'повертався' у сторону руху гравця, для цього нам потрібно повертати спрайт у напрямку руху гравця.
Але для початку нам потрібно створити змінну у нашому класі в яку ми запишемо об'єкт спрайта:
private AnimatedSprite2D AnimatedSprite2D;Потім, у _Ready() нам потрібно отримати AnimatedSprite2D нашого персонажа та записати його у змінну AnimatedSprite2D, для цього використаємо:
AnimatedSprite2D = GetNode<AnimatedSprite2D>("AnimatedSprite2D");Функція GetNodeNode типу Type з іменем name.
Зауважте!: name це насправді шлях до Node 'AnimatedSprite2D', зараз ми використовуємо відносний шлях, тобто - коли ми вказуємо 'AnimatedSprite2D' ми шукаємо цей Node у прямих нащадках Node-у з якого ми це робимо.
Після цього ми маємо змінну AnimatedSprite2D з AnimatedSprite2D у якому знаходиться спрайт нашого гравця.
Зараз наше завдання наступне - маніпулювати орієнтацією спрайта базуючись на напрямку руху нашого гравця, а саме:
Якщо гравець рухається вліво > повертаємо спрайт вліво; Якщо вправо > повертаємо спрайт вправо.
Це можна зробити шляхом маніпуляції bool змінної AnimatedSprite2D.FlipH:
if (velocity.X != 0)
{
if (velocity.X > 0)
{
AnimatedSprite2D.FlipH = true;
}
else if (velocity.X < 0)
{
AnimatedSprite2D.FlipH = false;
}
}Таким чином наш персонаж буде дивитись у сторону свого руху.
4 - Анімація спрайта також не реагує на стан персонажа.
Коли персонаж змінює свій стан (спокій/рух) анімація повинна змінюватись відповідно.
Для цього нам потрібно записувати назву анімації як ми хочем запустити у AnimatedSprite2D.Animation.
AnimatedSprite2D.Animation = "walk";Практичний код (доповнення до 3):
if (velocity.X != 0)
{
if (velocity.X > 0)
{
AnimatedSprite2D.FlipH = true;
}
else if (velocity.X < 0)
{
AnimatedSprite2D.FlipH = false;
}
AnimatedSprite2D.Animation = "walk";
}
else{
AnimatedSprite2D.Animation = "idle";
}walk та idle це назви анімацій які ми створили та додали у Настройка AnimatedSprite2D
Але зараз в нас є ще одна проблема - хоч і наші анімації запускаються вчасно, але їхня швидкість стала та не відповідає руху нашого персонажа.
Нам потрібно змінювати швидкість анімації відповідно до швидкості руху нашого персонажа:
AnimatedSprite2D.SpriteFrames.SetAnimationSpeed(anim_name, fps);anim_name - назва анімації швидкість якої ми хочемо змінити.
fps - швидкість анімації - FPS (Frames Per Second) (Кількість кадрів у секунду)
Питання - як це нам використати?
Нам потрібно перетворити швидкість персонажа у FPS.
Щоб це зробити нам потрібно знати наступне: максимальну швидкість персонажа та максимальний FPS.
Наприклад: Якщо макс. швидкість це 100, а макс. FPS це 20, то якщо персонаж рухається зі швидкістю 50 (50% від макс. швидкості) то FPS буде 10 (50% від макс. FPS).
На словах все просто, але як це реалізувати?
AnimatedSprite2D.SpriteFrames.SetAnimationSpeed(anim_name, max_fps * (Math.Abs(velocity.X) / max_speed));anim_name - назва анімації швидкість якрї ми хочемо змінити.
max_fps - максимальний FPS.
max_speed - максимальна швидкість.
using System;
using Godot;
public partial class CharacterBody2d : CharacterBody2D
{
private const float Speed = 500.0f;
private Vector2 Gravity = new Vector2(0, 50f);
private AnimatedSprite2D AnimatedSprite2D;
public override void _Ready()
{
AnimatedSprite2D = GetNode<AnimatedSprite2D>("AnimatedSprite2D");
}
public override void _PhysicsProcess(double delta)
{
Vector2 direction = GetDirection();
Vector2 velocity = Velocity;
velocity += direction * Speed * (float)delta;
velocity += Gravity;
if (velocity.X != 0)
{
if (velocity.X > 0)
{
AnimatedSprite2D.FlipH = true;
}
else if (velocity.X < 0)
{
AnimatedSprite2D.FlipH = false;
}
AnimatedSprite2D.Animation = "walk";
AnimatedSprite2D.SpriteFrames.SetAnimationSpeed("walk", 20 * (Math.Abs(velocity.X) / 1000));
}
else
{
AnimatedSprite2D.Animation = "idle";
}
if (direction.X == 1 && velocity.X < 0 || direction.X == -1 && velocity.X > 0)
{
velocity.X *= 0.90f;
}
else if (direction.X == 0)
{
velocity.X *= 0.90f;
}
GD.Print(direction.X);
GD.Print(velocity.X);
Velocity = velocity;
MoveAndSlide();
}
public Vector2 GetDirection()
{
Vector2 direction = new Vector2();
if (Input.IsPhysicalKeyPressed(Key.W))
{
direction.Y += -1;
}
if (Input.IsPhysicalKeyPressed(Key.S))
{
direction.Y += 1;
}
if (Input.IsPhysicalKeyPressed(Key.A))
{
direction.X += -1;
}
if (Input.IsPhysicalKeyPressed(Key.D))
{
direction.X += 1;
}
return direction;
}
}build info
- compiler
- 2.0.5.1 · 4054EF9BF6C4510A
- built
- 2026-11-08 14:31:25 UTC
WebUniverse