class_name OverworldCamera extends Camera3D const COLLISION_MARGIN:float = 0.3 @export_category("Target") @export var targetNode:Node3D = null @export var pivotOffset:Vector3 = Vector3(0, 1.2, 0) @export_category("Orbit") @export var distance:float = 10.0 @export var minDistance:float = 1.5 @export var orbitSensitivity:float = 120.0 @export var pitchMin:float = -10.0 @export var pitchMax:float = 70.0 @export_category("Collision") @export_flags_3d_physics var collisionMask:int = 1 var _yaw:float = 0.0 var _pitch:float = 30.0 func _process(delta:float) -> void: if targetNode == null: return var orbitInput:Vector2 = Input.get_vector( "camera_orbit_left", "camera_orbit_right", "camera_orbit_up", "camera_orbit_down" ) _yaw += orbitInput.x * orbitSensitivity * delta # Invert Y so stick-up = camera rises (bird's-eye) _pitch -= orbitInput.y * orbitSensitivity * delta _pitch = clamp(_pitch, pitchMin, pitchMax) var pivot:Vector3 = targetNode.global_transform.origin + pivotOffset var yawRad:float = deg_to_rad(_yaw) var pitchRad:float = deg_to_rad(_pitch) var dir:Vector3 = Vector3( sin(yawRad) * cos(pitchRad), sin(pitchRad), cos(yawRad) * cos(pitchRad) ) var desired:Vector3 = pivot + dir * distance var actual:Vector3 = _resolveCollision(pivot, desired) global_transform.origin = actual look_at(pivot, Vector3.UP) # Cast a ray from the pivot to the desired camera position. # If terrain blocks the shot, pull the camera in to just in front of the hit. func _resolveCollision(pivot:Vector3, desired:Vector3) -> Vector3: var space:PhysicsDirectSpaceState3D = get_world_3d().direct_space_state var query:PhysicsRayQueryParameters3D = PhysicsRayQueryParameters3D.create(pivot, desired) query.collision_mask = collisionMask var hit:Dictionary = space.intersect_ray(query) if hit.is_empty(): return desired var hitDir:Vector3 = (desired - pivot).normalized() var pulled:Vector3 = hit["position"] - hitDir * COLLISION_MARGIN var pulledDist:float = (pulled - pivot).length() if pulledDist < minDistance: return pivot + hitDir * minDistance return pulled