From 3fa4987bbb113a68baaf4aec454bdc46f4325c19 Mon Sep 17 00:00:00 2001 From: Nekojimi Date: Thu, 1 May 2025 17:48:19 +0100 Subject: [PATCH] Update turret script to account for gravity when firing. --- objects/buildings/turret.tscn | 8 ++-- objects/bullet.tscn | 1 + scripts/buildings/turret.gd | 73 ++++++++++++++++++++++++++++------- 3 files changed, 65 insertions(+), 17 deletions(-) diff --git a/objects/buildings/turret.tscn b/objects/buildings/turret.tscn index 36efd66..463ffd2 100644 --- a/objects/buildings/turret.tscn +++ b/objects/buildings/turret.tscn @@ -29,9 +29,11 @@ ammo_cap = 10 fire_cooldown_time = 0.2 reload_time = 1.0 bullet_scene = ExtResource("3_kpsgq") -fire_position = Vector3(0, 1, 0) -shot_impulse = 100.0 -lead_shot_factor = 0.01 +fire_position = Vector3(0, 2.1, 0) +shot_velocity = 20.0 +lead_shots = true +compensate_for_gravity = true +use_artillery_firing_solution = false [node name="MeshInstance3D" parent="." index="0"] material_override = SubResource("StandardMaterial3D_cgb0l") diff --git a/objects/bullet.tscn b/objects/bullet.tscn index fc9985a..272b13d 100644 --- a/objects/bullet.tscn +++ b/objects/bullet.tscn @@ -110,6 +110,7 @@ continuous_cd = true contact_monitor = true max_contacts_reported = 1 script = ExtResource("1_rsjgb") +damage_per_speed = 100.0 min_damage = 1.0 lifetime = 5.0 diff --git a/scripts/buildings/turret.gd b/scripts/buildings/turret.gd index 9bdb763..ed7ef13 100644 --- a/scripts/buildings/turret.gd +++ b/scripts/buildings/turret.gd @@ -1,12 +1,20 @@ extends Building +class_name Turret -@export var ammo_cap: int = 10 -@export var fire_cooldown_time: float = 0.2 -@export var reload_time: float = 1.0 -@export var bullet_scene: PackedScene = preload("res://objects/bullet.tscn") -@export var fire_position: Vector3 = Vector3(0,1,0) -@export var shot_impulse: float = 100 -@export var lead_shot_factor: float = 0.01 +@export_group("Ammunition") +@export var ammo_cap: int = 10 ## The number of shots per "magazine", i.e. reload. +@export var fire_cooldown_time: float = 0.2 ## The time between shots, in seconds. +@export var reload_time: float = 1.0 ## The time taken to reload, in seconds. + +@export_group("Bullet Spawning") +@export var bullet_scene: PackedScene = preload("res://objects/bullet.tscn") ## The entity to spawn as a bullet. +@export var fire_position: Vector3 = Vector3(0,1,0) ## The local position to fire from. + +@export_group("Ballistics") +@export var shot_velocity: float = 100 ## The velocity that bullets are fired at, in m/s. +@export var lead_shots: bool = true ## Turn on to make the turret aim ahead of moving targets. +@export var compensate_for_gravity: bool = true ## Turn on to make the turret aim above distant targets to arc it's shots. +@export var use_artillery_firing_solution: bool = false ## If compensating for gravity, turn this on to make the turret aim high instead of low. enum LoadState {UNLOADED, RELOADING, LOADED} @@ -23,7 +31,6 @@ func _process(delta: float) -> void: super(delta) if loaded == LoadState.UNLOADED: if consumer.take_any_item_from_storage(): - #Engine.time_scale = 1.0 loaded = LoadState.RELOADING if loaded == LoadState.RELOADING: reload_timer += delta @@ -70,17 +77,55 @@ func fire_at_target() -> void: #Engine.time_scale = 0.1 var bullet: RigidBody3D = bullet_scene.instantiate() add_child(bullet) - bullet.global_position = to_global(fire_position) + var bullet_pos: Vector3 = to_global(fire_position) + bullet.global_position = bullet_pos + + var target_range: float = bullet_pos.distance_to(current_target.global_position) + var shot_time: float = (target_range / shot_velocity) var aim_target: Vector3 = current_target.global_position - var target_range: float = global_position.distance_to(current_target.global_position) - aim_target += current_target.linear_velocity * lead_shot_factor * target_range - DebugDraw3D.draw_sphere(aim_target, 0.25, Color.RED, 0.1) + if lead_shots: + aim_target += current_target.linear_velocity * shot_time + target_range = bullet_pos.distance_to(aim_target) + DebugDraw3D.draw_sphere(aim_target, 0.25, Color.RED, shot_time) - bullet.look_at(aim_target) - bullet.apply_impulse((aim_target - bullet.global_position).normalized()*shot_impulse) + var fire_direction: Vector3 + if compensate_for_gravity: + var elevation: float = find_fire_angle(target_range, aim_target.y - bullet_pos.y, use_artillery_firing_solution) + var azimuth: float = Vector3.FORWARD.signed_angle_to(aim_target-global_position, Vector3.UP) + print("Shot params: θ=%d φ=%d" % [rad_to_deg(elevation), rad_to_deg(azimuth)]) + if elevation < -PI or elevation > PI: + print("wtf?? θ=%d" % rad_to_deg(elevation)) + return + fire_direction = Vector3.FORWARD + fire_direction = fire_direction.rotated(Vector3(1,0,0), elevation) + fire_direction = fire_direction.rotated(Vector3(0,1,0), azimuth) + else: + fire_direction = (aim_target - bullet_pos).normalized() + + DebugDraw3D.draw_arrow_ray(bullet_pos, fire_direction, target_range, Color.RED, 0.5, true, shot_time) + + bullet.look_at(bullet_pos+fire_direction) + bullet.linear_velocity = (-bullet.global_basis.z) * shot_velocity + #bullet.apply_impulse((aim_target - bullet.global_position).normalized()*shot_impulse) bullet.angular_velocity = (bullet.global_basis * Vector3(0,0,-10)) current_target = null ammo -= 1 if ammo <= 0: + Engine.time_scale = 1.0 loaded = LoadState.UNLOADED + +func find_fire_angle(distance: float, height: float, high_solution: bool = true) -> float: + # theta = arctan( ( (v^2) ± sqrt( v^4 - g*( g*x^2 + 2*y*v^2 ) ) ) / g*x ) + const g: float = 9.8 + var gx2: float = g * pow(distance, 2) + var twoyv2: float = 2 * height * pow(shot_velocity,2) + var v2: float = pow(shot_velocity, 2) + var v4: float = pow(shot_velocity, 4) + + var arc_x: float = g * distance + var arc_y_offs: float = sqrt(v4 - g * (gx2 + twoyv2)) + var arc_y: float = v2 + arc_y_offs * (1 if high_solution else -1) + + var theta: float = atan2(arc_y, arc_x) + return clamp(theta, -PI/2, PI/2)