rnd(M02): extract third-person camera rig

Work-Package: M02-RND-CAMERA-001
This commit is contained in:
2026-07-14 23:27:03 +04:00
parent f5bb64e6c7
commit 8c1cf9be36
17 changed files with 753 additions and 71 deletions
@@ -24,19 +24,10 @@ const ADT_TERRAIN_QUERY_SCRIPT := preload("res://src/gameplay/terrain/adt_terrai
@export var sprint_multiplier: float = 6.0
@export var flight_vertical_speed: float = 7.0
@export var acceleration: float = 28.0
@export var mouse_sensitivity: float = 0.003
@export var camera_pitch_min: float = deg_to_rad(-65.0)
@export var camera_pitch_max: float = deg_to_rad(35.0)
@export var camera_height: float = 1.7
@export var camera_distance: float = 8.0
@export var camera_min_distance: float = 2.0
@export var camera_max_distance: float = 18.0
@export var camera_zoom_step: float = 1.0
@export var ground_offset: float = 0.05
@export var ground_snap_speed: float = 24.0
@export var camera_pivot_path: NodePath = NodePath("CameraPivot")
@export var camera_path: NodePath = NodePath("CameraPivot/Camera3D")
@export var visual_path: NodePath = NodePath("Visual")
@export var character_model_path: String = "res://src/resources/characters/HUMAN/MALE/HumanMale.glb"
@export var character_class_id: int = 1
@@ -44,15 +35,11 @@ const ADT_TERRAIN_QUERY_SCRIPT := preload("res://src/gameplay/terrain/adt_terrai
@export var character_yaw_offset_degrees: float = 90.0
@export var animation_blend_time: float = 0.15
var _camera_pivot: Node3D
var _camera: Camera3D
var _camera_rig: ThirdPersonCameraRig
var _visual: Node3D
var _character_root: Node3D
var _animation_player: AnimationPlayer
var _active_animation := ""
var _captured := false
var _yaw := 0.0
var _pitch := deg_to_rad(-18.0)
var _player_input_source: PlayerInputSource
var _local_movement_controller: LocalPlayerMovementController
var _terrain_query: TerrainQuery
@@ -79,12 +66,12 @@ func _ready() -> void:
acceleration,
sprint_multiplier
)
_camera_pivot = get_node_or_null(camera_pivot_path) as Node3D
_camera = get_node_or_null(camera_path) as Camera3D
_camera_rig = get_node_or_null(camera_pivot_path) as ThirdPersonCameraRig
_visual = get_node_or_null(visual_path) as Node3D
if _camera:
_camera.current = true
_camera.far = 50000.0
if _camera_rig == null:
push_error("ThirdPersonWowController: ThirdPersonCameraRig not found at %s" % camera_pivot_path)
else:
_camera_rig.initialize_for_character(self)
if spawn_at_tile_center:
var spawn_tile = ADT_TILE_COORDINATE_SCRIPT.new(spawn_tile_x, spawn_tile_y)
var half_tile_size := COORDINATE_MAPPER_SCRIPT.ADT_TILE_SIZE_YARDS * 0.5
@@ -95,41 +82,20 @@ func _ready() -> void:
var spawn_ground_sample := _sample_ground_height(global_position)
if spawn_ground_sample.is_available:
global_position.y = spawn_ground_sample.height_units + ground_offset
_yaw = rotation.y
_apply_camera_transform()
func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed(PLAYER_INPUT_ACTIONS_SCRIPT.CAMERA_ROTATE):
_captured = true
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
elif event.is_action_released(PLAYER_INPUT_ACTIONS_SCRIPT.CAMERA_ROTATE):
_captured = false
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
elif event.is_action_pressed(PLAYER_INPUT_ACTIONS_SCRIPT.CAMERA_ZOOM_IN):
camera_distance = clampf(camera_distance - camera_zoom_step, camera_min_distance, camera_max_distance)
_apply_camera_transform()
elif event.is_action_pressed(PLAYER_INPUT_ACTIONS_SCRIPT.CAMERA_ZOOM_OUT):
camera_distance = clampf(camera_distance + camera_zoom_step, camera_min_distance, camera_max_distance)
_apply_camera_transform()
elif event.is_action_pressed(PLAYER_INPUT_ACTIONS_SCRIPT.RELEASE_CURSOR):
_captured = false
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED if _captured else Input.MOUSE_MODE_VISIBLE
elif event.is_action_pressed(PLAYER_INPUT_ACTIONS_SCRIPT.DEBUG_TOGGLE_FLIGHT) and not event.is_echo():
if _camera_rig != null:
_camera_rig.handle_camera_input(event)
if event.is_action_pressed(PLAYER_INPUT_ACTIONS_SCRIPT.DEBUG_TOGGLE_FLIGHT) and not event.is_echo():
_local_movement_controller.toggle_sandbox_flight()
if _captured and event is InputEventMouseMotion:
_yaw -= event.relative.x * mouse_sensitivity
_pitch = clampf(_pitch - event.relative.y * mouse_sensitivity, camera_pitch_min, camera_pitch_max)
rotation.y = _yaw
_apply_camera_transform()
func _physics_process(delta: float) -> void:
var move_intent := _player_input_source.sample_move_intent()
var godot_world_movement_basis := global_basis
if _local_movement_controller.is_flight_enabled and _camera_pivot:
godot_world_movement_basis = _camera_pivot.global_basis
if _local_movement_controller.is_flight_enabled and _camera_rig != null:
godot_world_movement_basis = _camera_rig.godot_world_flight_movement_basis()
global_position += _local_movement_controller.advance(move_intent, godot_world_movement_basis, delta)
if not _local_movement_controller.is_flight_enabled:
@@ -145,8 +111,6 @@ func _physics_process(delta: float) -> void:
_update_character_animation(horizontal_motion.length_squared() > 0.04)
_apply_camera_transform()
func _load_character_visual() -> void:
if character_model_path.is_empty() or _visual == null:
return
@@ -298,15 +262,6 @@ func _character_textures_dir(model_path: String) -> String:
return model_path.get_base_dir().path_join(model_path.get_file().get_basename() + "_textures")
func _apply_camera_transform() -> void:
if _camera_pivot:
_camera_pivot.position = Vector3(0.0, camera_height, 0.0)
_camera_pivot.rotation.x = _pitch
if _camera:
_camera.position = Vector3(0.0, 0.0, camera_distance)
_camera.rotation = Vector3.ZERO
func _sample_ground_height(godot_world_position: Vector3) -> TerrainGroundSample:
return _terrain_query.sample_ground_height(
GODOT_WORLD_POSITION_SCRIPT.new(