class_name Player extends CharacterBody3D ########### signal foundActionableNode signal noActionableNode ########### @export_category("Movement") @export var movement_enabled := true @export var walk_speed := 5.0 @export var acceleration := 30.0 @export var gravity := 10.0 @export_category("Camera") @export var camera_movement_enabled := true @export_range(0.0, 1.0) var mouse_sensitivity := 0.2 @export_range(0.0, 1.0) var mouse_sensitivity_x := 1.0 @export_range(0.0, 1.0) var mouse_sensitivity_y := 0.5 @export var look_at_rotation_speed := 3.0 enum {CAMERA_MOUSE_INPUT, CAMERA_JOYSTICK_INPUT} var _camera_input_method := CAMERA_MOUSE_INPUT var _camera_input_direction := Vector2.ZERO const _camera_highest_x_angle := PI / 2 const _camera_lowest_x_angle := -PI / 2 @onready var _camera: Camera3D = %Camera3D @onready var _phone = %Phone var actionable_node: Actionable = null var look_at_pos: Vector3 = Vector3.ZERO ########### func _physics_process(delta: float) -> void: if camera_movement_enabled: _camera.rotation.y -= _camera_input_direction.x * mouse_sensitivity_x * delta _camera.rotation.x -= _camera_input_direction.y * mouse_sensitivity_y * delta _camera.rotation.x = clamp( _camera.rotation.x, _camera_lowest_x_angle, _camera_highest_x_angle ) # reset mouse movement vector if mouse input if _camera_input_method == CAMERA_MOUSE_INPUT: _camera_input_direction = Vector2.ZERO elif look_at_pos != Vector3.ZERO: var target_vector: Vector3 = look_at_pos - _camera.global_position var target_angle_y = Vector3.FORWARD.signed_angle_to(target_vector, Vector3.UP) _camera.global_rotation.y = lerp_angle( _camera.global_rotation.y, target_angle_y, delta * look_at_rotation_speed ) var target_angle_x = atan2(target_vector.y, target_vector.length()) _camera.rotation.x = lerp_angle( _camera.rotation.x, target_angle_x, delta * look_at_rotation_speed ) if movement_enabled: if is_on_floor(): var move_direction := _get_player_move_direction() velocity = velocity.move_toward(move_direction * walk_speed, acceleration * delta) else: velocity += Vector3.DOWN * gravity * delta move_and_slide() func _unhandled_input(event: InputEvent) -> void: if Input.get_mouse_mode() != Input.MOUSE_MODE_CAPTURED: return if event is InputEventMouseMotion: _camera_input_method = CAMERA_MOUSE_INPUT _camera_input_direction = event.screen_relative * mouse_sensitivity ########### func phone_out(b: bool) -> void: _phone.visible = b func toggle_phone_out() -> void: if movement_enabled: _phone.visible = !_phone.visible func camera_look_at(pos: Vector3, snapTo: bool = false) -> void: if snapTo: _camera.look_at(pos, Vector3.UP) look_at_pos = pos func _get_player_move_direction() -> Vector3: var input_dir := Input.get_vector("player-left", "player-right", "player-up", "player-down") var forward := _camera.global_basis.z var right := _camera.global_basis.x var move_direction := (forward * input_dir.y + right * input_dir.x).normalized() move_direction.y = 0 return move_direction func _on_talking_area_body_entered(body: Node3D) -> void: if body is Actionable: print("Hello " + str(body) + ": " + body.get("prompt")) actionable_node = body foundActionableNode.emit(body) func _on_talking_area_body_exited(body: Node3D) -> void: if body is Actionable: if actionable_node == body: print("Goodbye " + str(body)) actionable_node = null noActionableNode.emit()