68 lines
2.2 KiB
GDScript
68 lines
2.2 KiB
GDScript
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"
|
|
)
|
|
var xMult:float = -1.0 if SETTINGS.invertCameraX else 1.0
|
|
var yMult:float = 1.0 if SETTINGS.invertCameraY else -1.0
|
|
_yaw += orbitInput.x * orbitSensitivity * delta * xMult
|
|
_pitch += orbitInput.y * orbitSensitivity * delta * yMult
|
|
_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
|