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