Огляд руху в 2D просторі

Вступ

Кожен початківець стикався з питанням: "Як я буду переміщувати свого персонажа?" Залежно від стилю гри, яку ви робите, ви можете мати особливі вимоги, але загалом рух у більшості 2D-ігор базується на невеликій кількості конструкцій.

Для цих прикладів ми використовуватимемо KinematicBody2D, але принципи будуть застосовуватися і до інших типів вузлів (Area2D, RigidBody2D).

Налаштування

У кожному прикладі нижче використовується однакова настройка сцени. Почніть з KinematicBody2D з двома нащадками: Sprite і CollisionShape2D. Ви можете використовувати піктограму Godot ("icon.png") для текстури спрайта, або будь-яке інше 2D зображення, яке у вас є.

Відкрийте Проект -> Параметри проекта та виберіть вкладку Карта введення. Додайте такі дії (щоб дізнатися більше дивіться Події Вводу):

../../_images/movement_inputs.png

8-бічний рух

У цьому випадку ви хочете, щоб користувач користувався чотирма клавішами (стрілками вгору/вліво/вниз/вправо, або W/A/S/D) для переміщення у вибраному напрямку. Назва «8-бічний рух» походить від того, що гравець може рухатися по діагоналі, натискаючи дві клавіші одночасно.

../../_images/movement_8way.gif

Додайте скрипт до кінематичного тіла з таким кодом:

extends KinematicBody2D

export (int) var speed = 200

var velocity = Vector2()

func get_input():
    velocity = Vector2()
    if Input.is_action_pressed("right"):
        velocity.x += 1
    if Input.is_action_pressed("left"):
        velocity.x -= 1
    if Input.is_action_pressed("down"):
        velocity.y += 1
    if Input.is_action_pressed("up"):
        velocity.y -= 1
    velocity = velocity.normalized() * speed

func _physics_process(delta):
    get_input()
    velocity = move_and_slide(velocity)
using Godot;
using System;

public class Movement : KinematicBody2D
{
    [Export] public int speed = 200;

    public Vector2 velocity = new Vector2();

    public void GetInput()
    {
        velocity = new Vector2();

        if (Input.IsActionPressed("right"))
            velocity.x += 1;

        if (Input.IsActionPressed("left"))
            velocity.x -= 1;

        if (Input.IsActionPressed("down"))
            velocity.y += 1;

        if (Input.IsActionPressed("up"))
            velocity.y -= 1;

        velocity = velocity.Normalized() * speed;
    }

    public override void _PhysicsProcess(float delta)
    {
        GetInput();
        velocity = MoveAndSlide(velocity);
    }
}

У функції get_input() ми перевіряємо чотири клавішні події та підсумовуємо їх, щоб отримати вектор швидкості. Це добре тому, що дві протилежні клавіші відміняють одна одну, але також призведе до швидшого руху діагоналі через додавання двох напрямків.

Ми можемо запобігти цьому нормалізувавши швидкість, тобто встановивши довжину вектора на 1, і помноживши на потрібну швидкість.

Порада

Якщо ви ніколи раніше не використовували векторну математику або вам потрібно освіжити знання, ви можете переглянути пояснення використання вектора у Godot на Vector math.

Примітка

Якщо вищенаведений код нічого не робить під час натискання клавіш, перевірте, чи правильно ви налаштували дії введення, як описано в частині Налаштування цього урока.

Обертання + рух

Цей тип руху іноді називають "Астероїдним стилем", оскільки він нагадує роботу цієї класичної аркадної гри. Натискання вліво/вправо обертає персонажа, тоді як вгору/вниз рухає його вперед, або назад, у будь-якому напрямку, до якого він спрямований.

../../_images/movement_rotate1.gif
extends KinematicBody2D

export (int) var speed = 200
export (float) var rotation_speed = 1.5

var velocity = Vector2()
var rotation_dir = 0

func get_input():
    rotation_dir = 0
    velocity = Vector2()
    if Input.is_action_pressed("right"):
        rotation_dir += 1
    if Input.is_action_pressed("left"):
        rotation_dir -= 1
    if Input.is_action_pressed("down"):
        velocity = Vector2(-speed, 0).rotated(rotation)
    if Input.is_action_pressed("up"):
        velocity = Vector2(speed, 0).rotated(rotation)

func _physics_process(delta):
    get_input()
    rotation += rotation_dir * rotation_speed * delta
    velocity = move_and_slide(velocity)
using Godot;
using System;

public class Movement : KinematicBody2D
{
    [Export] public int speed = 200;
    [Export] public float rotationSpeed = 1.5f;

    public Vector2 velocity = new Vector2();
    public int rotationDir = 0;

    public void GetInput()
    {
        rotationDir = 0;
        velocity = new Vector2();

        if (Input.IsActionPressed("right"))
            rotationDir += 1;

        if (Input.IsActionPressed("left"))
            rotationDir -= 1;

        if (Input.IsActionPressed("down"))
            velocity = new Vector2(-speed, 0).Rotated(Rotation);

        if (Input.IsActionPressed("up"))
            velocity = new Vector2(speed, 0).Rotated(Rotation);

        velocity = velocity.Normalized() * speed;
    }

    public override void _PhysicsProcess(float delta)
    {
        GetInput();
        Rotation += rotationDir * rotationSpeed * delta;
        velocity = MoveAndSlide(velocity);
    }
}

Тут ми додали дві нові змінні для відстеження нашого напрямку обертання та швидкості. Знову ж таки, натискання обох клавіш одночасно призведе до скасування та не призведе до обертання. Обертання застосовується безпосередньо до властивості тіла rotation.

Для встановлення швидкості ми використовуємо метод Vector2.rotated() так, щоб він вказував у тому ж напрямку, що і тіло. rotated() є корисною векторною функцією, яку ви можете використовувати в багатьох випадках, в яких за інших умов довелося б застосувати тригонометричні функції.

Обертання + рух (мишкою)

Цей стиль руху є різновидом попереднього. Цього разу напрямок задається положенням мишки, замість клавіатури. Персонаж завжди буде "дивитись" на курсор мишки. Однак вхідні дані вперед/назад залишаються незмінними.

../../_images/movement_rotate2.gif
extends KinematicBody2D

export (int) var speed = 200

var velocity = Vector2()

func get_input():
    look_at(get_global_mouse_position())
    velocity = Vector2()
    if Input.is_action_pressed("down"):
        velocity = Vector2(-speed, 0).rotated(rotation)
    if Input.is_action_pressed("up"):
        velocity = Vector2(speed, 0).rotated(rotation)

func _physics_process(delta):
    get_input()
    velocity = move_and_slide(velocity)
using Godot;
using System;

public class Movement : KinematicBody2D
{
    [Export] public int speed = 200;

    public Vector2 velocity = new Vector2();

    public void GetInput()
    {
        LookAt(GetGlobalMousePosition());
        velocity = new Vector2();

        if (Input.IsActionPressed("down"))
            velocity = new Vector2(-speed, 0).Rotated(Rotation);

        if (Input.IsActionPressed("up"))
            velocity = new Vector2(speed, 0).Rotated(Rotation);

        velocity = velocity.Normalized() * speed;
    }

    public override void _PhysicsProcess(float delta)
    {
        GetInput();
        velocity = MoveAndSlide(velocity);
    }
}

Тут ми використовуємо метод Node2D look_at(), щоб спрямувати гравця на певну позицію. Без цієї функції ви можете отримати той самий ефект, встановивши кут так:

rotation = get_global_mouse_position().angle_to_point(position)
var rotation = GetGlobalMousePosition().AngleToPoint(Position);

Клацай і рухайся

Цей останній приклад використовує лише мишку для управління персонажем. Натискання на екрані призведе до переміщення гравця до цільового місця.

../../_images/movement_click.gif
extends KinematicBody2D

export (int) var speed = 200

var target = Vector2()
var velocity = Vector2()

func _input(event):
    if event.is_action_pressed("click"):
        target = get_global_mouse_position()

func _physics_process(delta):
    velocity = position.direction_to(target) * speed
    # look_at(target)
    if position.distance_to(target) > 5:
        velocity = move_and_slide(velocity)
using Godot;
using System;

public class Movement : KinematicBody2D
{
    [Export] public int speed = 200;

    public Vector2 target = new Vector2();
    public Vector2 velocity = new Vector2();

    public override void _Input(InputEvent @event)
    {
        if (@event.IsActionPressed("click"))
        {
            target = GetGlobalMousePosition();
        }
    }

    public override void _PhysicsProcess(float delta)
    {
        velocity = Position.DirectionTo(target) * speed;
        // LookAt(target);
        if (Position.DistanceTo(target) > 5)
        {
            velocity = MoveAndSlide(velocity);
        }
    }
}

Зверніть увагу на перевірку distance_to(), яку ми робимо перед переміщенням. Без цього тесту тіло буде "тремтіти", досягнувши цільового положення, коли воно просунеться трохи задалеко, спробує повернутися назад, знову проскочивши цільове положення, і так без кінця.

Знявши коментар з рядка look_at(), ви також повернете тіло в напрямку руху, якщо вам так більше подобається.

Порада

Цей прийом також може бути використаний як основа "відстежуючого" персонажа. Позицією target (ціль) може бути будь-який об'єкт, до якого ви хочете перейти.

Підсумок

Ці зразки коду можуть стати корисними в якості основи для ваших власних проектів. Сміливо використовуйте їх і експериментуйте з ними, щоб побачити, що ви можете зробити.

Цей зразок проекту ви можете завантажити тут: 2D_movement_demo.zip