From 906e161b678507cd1ac43c0bb5acbf2b437f63f9 Mon Sep 17 00:00:00 2001 From: Rob Kelly Date: Mon, 9 Dec 2024 14:40:23 -0700 Subject: [PATCH] Projectile arc computes local gravity at each step --- src/ui/3d/projectile_arc/projectile_arc.gd | 65 +++++++++++++++++++++- 1 file changed, 63 insertions(+), 2 deletions(-) diff --git a/src/ui/3d/projectile_arc/projectile_arc.gd b/src/ui/3d/projectile_arc/projectile_arc.gd index 6ec7898..18618ab 100644 --- a/src/ui/3d/projectile_arc/projectile_arc.gd +++ b/src/ui/3d/projectile_arc/projectile_arc.gd @@ -22,7 +22,7 @@ const PUTT_ATTRITION := 0.8325 # green? ## If enabled, project a linear putt instead of an arcing shot @export var putt_projection := false -@export_category("Collision") +@export_category("Collision & Physics") ## Enables collision checking. Projection will end at the point where a collision is detected. ## Uses continuous collision detection. @export var check_collision := true @@ -32,6 +32,10 @@ const PUTT_ATTRITION := 0.8325 # green? ## This should probably include the ball! @export var excluded_bodies: Array[CollisionObject3D] = [] +## Enables checking local gravity at each point along the trajectory. +## If disabled, global gravity will be used instead. +@export var check_gravity := true + var _tick_counter := 0 var _debug_points: Array[Vector3] = [] @@ -73,8 +77,13 @@ func _process(_delta: float) -> void: # TODO: smooth curve with bezier handles path.curve.add_point(pos - global_position) + # Get local gravity if enabled + var local_gravity := gravity * gravity_vec + if check_gravity: + local_gravity = _get_gravity(pos) + # Integrate projectile path - vel += gravity * gravity_vec * time_step + vel += local_gravity * time_step var next_pos := pos + vel * time_step # Collision @@ -110,6 +119,58 @@ func _process(_delta: float) -> void: (%DebugDraw as CanvasItem).queue_redraw() +func _get_gravity(point: Vector3) -> Vector3: + # Start with global gravity + var local_gravity := gravity * gravity_vec + + # TODO this is awful, surely there has to be a better way than this!!! + # Get areas at point + var point_params := PhysicsPointQueryParameters3D.new() + point_params.collide_with_areas = true + point_params.collide_with_bodies = false + point_params.collision_mask = collision_mask + point_params.position = point + var collisions := get_world_3d().direct_space_state.intersect_point(point_params) + var gravity_areas: Array[Area3D] = [] + gravity_areas.assign( + collisions.map(func(d: Dictionary) -> Area3D: return d["collider"] as Area3D) + ) + gravity_areas.sort_custom(func(a: Area3D, b: Area3D) -> bool: return a.priority < b.priority) + + # Iteratively integrate gravity + for area: Area3D in gravity_areas: + var point_local := point - area.global_position + var area_gravity: Vector3 + if area.gravity_point: + # TODO: `point` may need to be local + var v := area.transform * area.gravity_direction - point_local + if area.gravity_point_unit_distance > 0: + var v_sq := v.length_squared() + if v_sq > 0: + area_gravity = ( + v.normalized() + * area.gravity + * pow(area.gravity_point_unit_distance, 2) + / v_sq + ) + else: + area_gravity = Vector3.ZERO + else: + area_gravity = v.normalized() * area.gravity + else: + area_gravity = area.gravity * area.gravity_direction + + match area.gravity_space_override: + Area3D.SPACE_OVERRIDE_COMBINE, Area3D.SPACE_OVERRIDE_COMBINE_REPLACE: + local_gravity += area_gravity + Area3D.SPACE_OVERRIDE_REPLACE_COMBINE, Area3D.SPACE_OVERRIDE_REPLACE: + local_gravity = area_gravity + Area3D.SPACE_OVERRIDE_COMBINE_REPLACE, Area3D.SPACE_OVERRIDE_REPLACE: + break + + return local_gravity + + func _on_visibility_changed() -> void: # Force update as soon as visible _tick_counter = 0