73 lines
2.7 KiB
GDScript
73 lines
2.7 KiB
GDScript
extends Node2D
|
|
|
|
const PROJECTILE := preload("res://scenes/game/weapon_projectile.tscn")
|
|
|
|
func _ready() -> void:
|
|
seed(1234)
|
|
# Both stationary and crossing targets should be intercepted on the first pass.
|
|
for target_velocity in [Vector2.ZERO, Vector2(0, 55)]:
|
|
var target := CharacterBody2D.new()
|
|
target.position = Vector2(350, 70)
|
|
target.velocity = target_velocity
|
|
add_child(target)
|
|
var missile := _missile(target, 1200.0)
|
|
var closest := INF
|
|
for step in 240:
|
|
var previous: Vector2 = missile.position
|
|
target.position += target_velocity / 120.0
|
|
missile._physics_process(1.0 / 120.0)
|
|
var near := Geometry2D.get_closest_point_to_segment(target.position, previous, missile.position)
|
|
closest = minf(closest, near.distance_to(target.position))
|
|
assert(absf(missile._turn_velocity) <= missile.homing_max_turn_speed + 0.001)
|
|
if closest < 18.0 or missile.is_queued_for_deletion():
|
|
break
|
|
assert(closest < 18.0, "Missile missed a reachable intercept: %.2f" % closest)
|
|
missile.free()
|
|
target.free()
|
|
|
|
# Compare actual flight direction against the previous turn limits.
|
|
var side_target := Node2D.new()
|
|
side_target.position = Vector2(100, 200)
|
|
add_child(side_target)
|
|
var faster := _missile(side_target, 600.0)
|
|
var previous := _missile(side_target, 600.0)
|
|
previous.homing_turn_acceleration = 2.3
|
|
previous.homing_max_turn_speed = 1.7
|
|
previous.missile_steering_acceleration = 520.0
|
|
for step in 48:
|
|
faster._physics_process(1.0 / 120.0)
|
|
previous._physics_process(1.0 / 120.0)
|
|
assert(faster._heading > previous._heading * 1.5)
|
|
assert(faster._flight_velocity.angle() > previous._flight_velocity.angle() * 1.5)
|
|
assert(faster.homing_target == side_target)
|
|
faster.free()
|
|
previous.free()
|
|
side_target.free()
|
|
|
|
# Preserve the original pursuit behavior beyond acquisition range.
|
|
var far_target := Node2D.new()
|
|
far_target.position = Vector2(5000, 0)
|
|
far_target.add_to_group("enemies")
|
|
add_child(far_target)
|
|
var limited := _missile(far_target, 200.0)
|
|
limited._has_locked_target = true
|
|
for step in 120:
|
|
limited._physics_process(1.0 / 120.0)
|
|
if limited.is_queued_for_deletion():
|
|
break
|
|
assert(not limited.is_queued_for_deletion() and limited.homing_target == far_target)
|
|
assert(limited._has_locked_target)
|
|
limited.free()
|
|
far_target.free()
|
|
print("MISSILE_GUIDANCE_TEST: PASS (stationary and moving intercepts, faster heading and actual flight turn, original target pursuit)")
|
|
get_tree().quit()
|
|
|
|
func _missile(target: Node2D, flight_range: float) -> Area2D:
|
|
var missile := PROJECTILE.instantiate() as Area2D
|
|
missile.configure(Vector2.ZERO, Vector2.RIGHT, ItemDefinition.WeaponKind.MISSILE, 400.0, 10.0, flight_range, target)
|
|
missile.collision_mask = 0
|
|
add_child(missile)
|
|
missile.set_physics_process(false)
|
|
missile.set_process(false)
|
|
return missile
|