Движение персонажа по сетке в Godot

Проблема

Вам нужно сделать персонажа в игре, который будет перемещаться по сетке (GRID-BASED MOVEMENT).

Решение

Движение по сетке или тайловое движение означает, что положение персонажа ограничено. Он может стоять только на определенном тайле и не может между двумя тайлами.

Настройка персонажа

Вот узлы, которые мы будем использовать для игрока:

  • Area2D («Игрок»): Использование Area2D означает, что мы можем обнаруживать перекрытия (для подбора объектов или столкновения с врагами).
    • Sprite2D: Здесь можно использовать спрайтовый лист (мы настроим анимацию ниже).
    • CollisionShape2D: Не делайте хитбокс слишком большим. Поскольку игрок будет стоять в центре тайла, перекрытия будут от центра.
    • RayCast2D: Для проверки, возможно ли двигаться в заданном направлении.
    • AnimationPlayer: Для воспроизведения анимации(й) ходьбы персонажа.

Добавьте некоторые действия ввода в карту ввода. Мы будем использовать «вверх», «вниз», «влево» и «вправо» для этого примера.

Основное движение

Мы начнем с настройки движения от тайла к тайлу без анимаций или интерполяции.

GRID-extends Area2D

var tile_size = 64
var inputs = {"right": Vector2.RIGHT,
            "left": Vector2.LEFT,
            "up": Vector2.UP,
            "down": Vector2.DOWN}BASED MOVEMENT

tile_size должен быть установлен так, чтобы соответствовать размеру ваших клеток. В более крупном проекте его можно установить в основной сцене при создании экземпляра игрока. В приведенном ниже примере мы используем тайлы размером 64×64.

Словарь inputs сопоставляет имена действий ввода с векторами направлений. Убедитесь, что здесь и в Карте ввода имена написаны одинаково (регистр имеет значение!).

func _ready():
    position = position.snapped(Vector2.ONE * tile_size)
    position += Vector2.ONE * tile_size/2

snapped() позволяет нам «округлить» позицию до ближайшего инкремента тайла, и добавление половинного значения тайла гарантирует, что игрок находится в центре тайла.

func _unhandled_input(event):
    for dir in inputs.keys():
        if event.is_action_pressed(dir):
            move(dir)

func move(dir):
    position += inputs[dir] * tile_size

Вот сам код движения. Когда происходит событие ввода, мы проверяем четыре направления, чтобы определить, которое соответствует, затем передаем его функции move() для изменения позиции.

Столкновения

Теперь мы можем добавить преграды. Вы можете добавить StaticBody2D вручную, чтобы создать преграды (включите привязку, чтобы убедиться, что они выровнены по сетке), или использовать TileMap (с определенными столкновениями), как показано в приведенном ниже примере.

Мы будем использовать RayCast2D, чтобы определить, разрешено ли движение на следующий тайл.

onready var ray = $RayCast2D

func move(dir):
    ray.target_position = inputs[dir] * tile_size
    ray.force_raycast_update()
    if !ray.is_colliding():
        position += inputs[dir] * tile_size

При изменении свойства target_position луча, физический движок не будет пересчитывать столкновения до следующего кадра. force_raycast_update() позволяет немедленно обновить состояние луча. Если он не сталкивается, то мы разрешаем движение.

Примечание! Еще один распространенный метод — использование 4 отдельных лучей, по одному на каждое направление.

Анимация движения

Наконец, мы можем интерполировать позицию между тайлами, придавая движению плавность. Мы будем использовать узел Tween для анимации свойства position.


var animation_speed = 3
var moving = false

Добавьте ссылку на узел Tween и переменную для установки нашей скорости движения.

func _unhandled_input(event):
    if moving:
        return
    for dir in inputs.keys():
        if event.is_action_pressed(dir):
            move(dir)

Мы будем игнорировать любой ввод во время выполнения tween и уберем прямое изменение позиции, чтобы tween мог им управлять.

func move(dir):
    ray.target_position = inputs[dir] * tile_size
    ray.force_raycast_update()
    if !ray.is_colliding():
        #position += inputs[dir] * tile_size
        var tween = create_tween()
        tween.tween_property(self, "position",
            position + inputs[dir] * tile_size, 1.0/animation_speed).set_trans(Tween.TRANS_SINE)
        moving = true
        await tween.finished
        moving = false

Экспериментируйте с различными переходами tween для различных эффектов движения.

Скачайте этот проект

Скачайте код проекта здесь: https://github.com/godotrecipes/2d_grid_movement/

Grid-Based Movement In less than 3 minutes Using Godot 4.0!Grid-Based Movement In less than 3 minutes Using Godot 4.0!

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

пятнадцать − пять =

Прокрутить вверх