142 lines
3.9 KiB
GDScript3
Raw Normal View History

2024-02-12 15:58:17 +00:00
class_name Camera
2024-01-16 22:01:16 +00:00
extends Camera3D
2024-02-21 19:15:11 +00:00
@export var center_offset := Vector3(0, 1.4, 0)
var center: Vector3
var look_offset: Vector3
var target_position: Vector3
@export_group("Collision")
@export var min_height_above_ground: float = 0
@export_flags_3d_physics var collision_mask := 1
var occlusion: Dictionary
var occlusion_distance := 0.0
@export_group("Rotation")
@export var sensitivity: float = 0.2
@export var min_angle_x: float = -80.0
@export var max_angle_x: float = 45.0
var angle_x: float
var angle_y: float
var look_enabled: bool
2024-02-09 23:10:51 +00:00
@export_group("Zoom")
@export var zoom_speed := 0.5
@export var zoom_interpolation := 10.0
2024-02-21 19:15:11 +00:00
@export var zoom_min := 2.5
2024-02-09 23:10:51 +00:00
@export var zoom_max := 8.0
2024-02-21 19:15:11 +00:00
var distance: float
2024-02-09 23:10:51 +00:00
var target_distance: float
2024-02-07 22:02:27 +00:00
2024-01-16 22:01:16 +00:00
func _ready():
2024-02-21 19:15:11 +00:00
distance = 7.5
target_distance = distance
sensitivity = deg_to_rad(sensitivity)
min_angle_x = deg_to_rad(min_angle_x)
max_angle_x = deg_to_rad(max_angle_x)
2024-01-16 22:01:16 +00:00
Global.camera = self
2024-02-21 19:15:11 +00:00
update_look_offset()
func _unhandled_input(event):
if !event.is_action("look"):
return
match event.is_pressed():
true:
DisplayServer.mouse_set_mode(DisplayServer.MOUSE_MODE_CAPTURED)
look_enabled = true
false:
DisplayServer.mouse_set_mode(DisplayServer.MOUSE_MODE_VISIBLE)
look_enabled = false
2024-01-16 22:01:16 +00:00
2024-02-12 10:53:16 +00:00
func _input(event):
if event.is_action_pressed("zoom_in", true):
2024-02-09 23:10:51 +00:00
target_distance -= zoom_speed
on_distance_changed()
2024-02-12 10:53:16 +00:00
get_viewport().set_input_as_handled()
2024-02-21 19:15:11 +00:00
return
2024-02-09 23:10:51 +00:00
2024-02-12 10:53:16 +00:00
if event.is_action_pressed("zoom_out", true):
2024-02-09 23:10:51 +00:00
target_distance += zoom_speed
on_distance_changed()
2024-02-12 10:53:16 +00:00
get_viewport().set_input_as_handled()
2024-02-21 19:15:11 +00:00
return
if !look_enabled:
return
if not event is InputEventMouseMotion:
return
angle_x -= event.relative.y * sensitivity
angle_y -= event.relative.x * sensitivity
angle_x = clampf(angle_x, min_angle_x, max_angle_x)
update_look_offset()
func _process(delta):
if !Global.player:
return
center = Global.player.position + center_offset
distance = Math.dampf(distance, target_distance, zoom_interpolation * delta)
target_position = center + look_offset * distance
if min_height_above_ground > 0:
var result := check_terrain()
if result && target_position.y - result.position.y < min_height_above_ground:
target_position.y = result.position.y + min_height_above_ground
global_position = target_position
check_occlusion()
if occlusion_distance < distance:
global_position = center + look_offset * occlusion_distance
look_at(center)
2024-02-07 22:02:27 +00:00
2024-02-09 23:10:51 +00:00
func on_distance_changed():
target_distance = clampf(target_distance, zoom_min, zoom_max)
Global.camera_attributes.dof_blur_far_distance = target_distance + 1.0
2024-02-07 22:02:27 +00:00
2024-02-21 19:15:11 +00:00
func update_look_offset():
look_offset = get_offset(Vector3.BACK)
2024-02-12 18:35:37 +00:00
2024-02-21 19:15:11 +00:00
func get_offset(vec: Vector3) -> Vector3:
vec = vec.rotated(Vector3.RIGHT, angle_x)
vec = vec.rotated(Vector3.UP, angle_y)
return vec
func check_terrain() -> Dictionary:
var space := get_world_3d().direct_space_state
var from := Vector3(target_position.x, 100, target_position.z)
var to := Vector3(target_position.x, -100, target_position.z)
var query := PhysicsRayQueryParameters3D.create(from, to, collision_mask)
return space.intersect_ray(query)
2024-02-12 15:58:17 +00:00
2024-02-21 19:15:11 +00:00
func check_occlusion():
occlusion_distance = INF
var rect := get_viewport().get_visible_rect()
var space := get_world_3d().direct_space_state
send_ray_to_screen(rect.position, space)
send_ray_to_screen(Vector2(rect.end.x, rect.position.y), space)
send_ray_to_screen(rect.get_center(), space)
send_ray_to_screen(Vector2(rect.position.x, rect.end.y), space)
send_ray_to_screen(rect.end, space)
2024-02-12 18:35:37 +00:00
2024-02-21 19:15:11 +00:00
func send_ray_to_screen(point: Vector2, space: PhysicsDirectSpaceState3D):
var screen_position := project_position(point, near)
var query := PhysicsRayQueryParameters3D.create(center, screen_position, collision_mask)
var hit := space.intersect_ray(query)
2024-02-12 15:58:17 +00:00
2024-02-21 19:15:11 +00:00
if !hit:
2024-02-09 23:10:51 +00:00
return
2024-02-21 19:15:11 +00:00
var hit_point := hit.position as Vector3
var hit_distance := (hit_point - center).length()
if hit_distance < occlusion_distance:
occlusion = hit
occlusion_distance = hit_distance