Преследующая ракета

Проблема

Вам нужна «самонаводящаяся ракета» — снаряд, который будет преследовать движущуюся цель.

Решение

В этом примере мы используем узел Area2D для снаряда. Области (Area) обычно хорошо подходят для пуль, так как нам нужно определять, когда они сталкиваются с чем-либо. Если вам также нужна пуля, которая может отскакивать/рикошетить, лучше выбрать узел типа PhysicsBody.

Настройка узла и поведение ракеты такие же, как и для «обычной» пули. Если вы создаете много типов пуль, вы можете использовать наследование, чтобы все ваши снаряды основывались на одной базовой настройке.

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

 Area2D: Missile
     Sprite2D
     CollisionShape2D
     Timer: Lifetime

Для текстуры вы можете использовать любое изображение, которое вам нравится. Вот пример:

Настройте узлы и задайте текстуру для спрайта, а также форму для столкновений. Убедитесь, что вы повернули узел Sprite2D на 90°, чтобы он был направлен вправо, совпадая с «направлением вперед» родительского узла.

Добавьте скрипт и подключите сигнал body_entered узла Area2D и сигнал timeout узла Timer.

Вот начальный скрипт:

extends Area2D

export var speed = 350

var velocity = Vector2.ZERO
var acceleration = Vector2.ZERO

func start(_transform):
    global_transform = _transform
    velocity = transform.x * speed

func _physics_process(delta):
    velocity += acceleration * delta
    velocity = velocity.clamped(speed)
    rotation = velocity.angle()
    position += velocity * delta

func _on_Missile_body_entered(body):
    queue_free()

func _on_Lifetime_timeout():
    queue_free()

Это создает «обычную» ракету, которая движется по прямой линии после выстрела. Чтобы использовать этот снаряд, создайте его экземпляр и вызовите метод start(), передав нужный Transform2D, чтобы задать его позицию и направление.

Для получения дополнительной информации см. раздел с похожими рецептами ниже.

Чтобы изменить поведение на преследование цели, мы будем использовать ускорение. Однако мы не хотим, чтобы ракета могла «разворачиваться на месте», поэтому добавим переменную для управления «силой поворота». Это задаст радиус поворота ракеты, который можно настроить для разных типов поведения. Также нам понадобится переменная для цели, чтобы ракета знала, за чем ей нужно следовать. Мы зададим это в методе start():

export var steer_force = 50.0

var target = null

func start(_transform, _target):
    target = _target
    ...

Чтобы изменить направление ракеты для движения к цели, ей нужно ускоряться в этом направлении (ускорение — это изменение скорости). Ракета «хочет» двигаться прямо к цели, но её текущая скорость направлена в другую сторону. Используя немного векторной математики, мы можем найти эту разницу:

Зеленая стрелка показывает необходимое изменение скорости (то есть ускорение). Однако если ракета будет поворачивать мгновенно, это будет выглядеть неестественно, поэтому длину вектора «поворота» нужно ограничить. Для этого используется переменная steer_force.

Вот функция для расчета этого ускорения. Обратите внимание, что если цель отсутствует, поворот не будет происходить, и ракета продолжит движение по прямой линии.

func seek():
    var steer = Vector2.ZERO
    if target:
        var desired = (target.position - position).normalized() * speed
        steer = (desired - velocity).normalized() * steer_force
    return steer

Наконец, полученная сила поворота должна быть применена в методе _physics_process():

func _physics_process(delta):
    acceleration += seek()
    velocity += acceleration * delta
    velocity = velocity.clamped(speed)
    rotation = velocity.angle()
    position += velocity * delta

Вот пример результата с небольшими визуальными эффектами, такими как дым от частиц и взрывы:

Вот полный скрипт, включая описанные выше эффекты. Подробности см. в связанных рецептах.

extends Area2D

export var speed = 350
export var steer_force = 50.0

var velocity = Vector2.ZERO
var acceleration = Vector2.ZERO
var target = null

func start(_transform, _target):
    global_transform = _transform
    rotation += rand_range(-0.09, 0.09)
    velocity = transform.x * speed
    target = _target

func seek():
    var steer = Vector2.ZERO
    if target:
        var desired = (target.position - position).normalized() * speed
        steer = (desired - velocity).normalized() * steer_force
    return steer

func _physics_process(delta):
    acceleration += seek()
    velocity += acceleration * delta
    velocity = velocity.clamped(speed)
    rotation = velocity.angle()
    position += velocity * delta

func _on_Missile_body_entered(body):
    explode()

func _on_Lifetime_timeout():
    explode()

func explode():
    $Particles2D.emitting = false
    set_physics_process(false)
    $AnimationPlayer.play("explode")
    await $AnimationPlayer.animation_finished
    queue_free()

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

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

19 − восемь =

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