phs-galaxy/ShootyEnemy.gd

112 lines
3.7 KiB
GDScript

extends KinematicBody
# An enemy which shoots at the future player position and moves between two targets within a given
# amount of time.
export(NodePath) var player_node
onready var player = get_node(player_node)
export(NodePath) var solar_system_node
onready var solar_system = get_node(solar_system_node)
export(Array, NodePath) var target_paths
export var target_fly_time := 5.0
var time_passed_since_fly_start := 0.0
var current_target_index = 0
var bullet_scene = preload("res://Bullet.tscn")
var bullet_velocity = 40.0
var shoot_target
var velocity := Vector3.ZERO
func _ready():
# Shoot everytime the timer times out
$ShotTimer.connect("timeout", self, "shoot_bullet")
# Set up pathing: Start at the first target
global_transform.origin = get_node(target_paths[0]).global_transform.origin
_set_velocity_to_fly_towards_next_target()
# Picks the next target position from the `target_paths` list and set the `velocity` appropriately
# so that the target will be reached after `target_fly_time`.
func _set_velocity_to_fly_towards_next_target():
current_target_index = (current_target_index + 1) % target_paths.size()
# Vector from here to the new target
var vector_to_next = (get_node(target_paths[current_target_index]).global_transform.origin
- global_transform.origin)
# s = v * t --> v = s / t
velocity = vector_to_next / target_fly_time
time_passed_since_fly_start = 0.0
func _physics_process(delta):
# Project the player's position into the future
shoot_target = _get_future_position(
player.get_center(),
player.velocity - velocity
)
# Look at the target that will be shot at, with the gravity as the down vector
if shoot_target:
var gravity = solar_system.get_gravity_acceleration(global_transform.origin)
look_at(shoot_target, gravity)
# Have we rearched the target?
time_passed_since_fly_start += delta
if time_passed_since_fly_start >= target_fly_time:
# If so, fly towards the next target
_set_velocity_to_fly_towards_next_target()
# Apply velocity
global_transform.origin += velocity * delta
func shoot_bullet():
if not shoot_target:
# Player can't be hit right now, abort
return
# Add a bullet
var instance = bullet_scene.instance()
get_tree().get_root().add_child(instance)
# Make the bullet start at the current position of this object and fly towards the target.
# The own velocity is added because of galilean relativity.
instance.global_transform.origin = global_transform.origin - global_transform.basis.z
instance.velocity = velocity + (shoot_target - global_transform.origin).normalized() * bullet_velocity
# Return the position at which a bullet fired forwards and with the velocity `bullet_velocity` would
# intersect with the object that is at `position` and moving with the given `velocity`
func _get_future_position(position, velocity):
# Solution to the quadratic formula gives us the time at which the player would be hit
var a = pow(velocity.x, 2) + pow(velocity.y, 2) + pow(velocity.z, 2) - pow(bullet_velocity, 2)
var b = 2 * (velocity.x * (position.x - global_transform.origin.x)
+ velocity.y * (position.y - global_transform.origin.y)
+ velocity.z * (position.z - global_transform.origin.z))
var c = pow(position.x - global_transform.origin.x, 2.0) \
+ pow(position.y - global_transform.origin.y, 2.0) \
+ pow(position.z - global_transform.origin.z, 2.0)
var discriminant = pow(b, 2) - 4 * a * c
if (discriminant < 0): return null # Can't hit the target :(
var t1 = (-b + sqrt(discriminant)) / (2 * a)
var t2 = (-b - sqrt(discriminant)) / (2 * a)
# Choose the smallest positive t value
var t = min(t1, t2)
if t < 0: t = max(t1, t2)
# Return the position given by the time we calculated
return position + t * velocity