Projectile arc computes local gravity at each step

This commit is contained in:
Rob Kelly 2024-12-09 14:40:23 -07:00
parent 60dd5bf659
commit 906e161b67
1 changed files with 63 additions and 2 deletions

View File

@ -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