extends CharacterBody3D
@export var SPEED: float = 12.0
@export var GRAVITY: float = 20.0
# Текстуры спины и лица (назначаются в Инспекторе)
@export var texture_back: Texture2D
@export var texture_front: Texture2D
# Чувствительность мыши
@export var MOUSE_SENSITIVITY: float = 0.003
# Настройки зума камеры (дистанция)
@export var MIN_ZOOM: float = 2.0
@export var MAX_ZOOM: float = 12.0
@export var ZOOM_SPEED: float = 0.5
# Ограничения вертикального наклона камеры (в радианах)
@export var MIN_PITCH: float = deg_to_rad(-60.0)
@export var MAX_PITCH: float = deg_to_rad(60.0)
@onready var sprite: Sprite3D = $Sprite3D
@onready var camera_pivot: Node3D = $CameraPivot
@onready var camera: Camera3D = $CameraPivot/Camera3D
func _ready() -> void:
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
func _input(event: InputEvent) -> void:
# 1. ПКМ включает/выключает вращение камеры
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_RIGHT:
if event.pressed:
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
else:
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
# 2. ЗУМ (Масштабирование колёсиком мыши)
elif event.pressed and camera:
if event.button_index == MOUSE_BUTTON_WHEEL_UP:
# Приближаем камеру
camera.position.z = clamp(camera.position.z - ZOOM_SPEED, MIN_ZOOM, MAX_ZOOM)
elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
# Отдаляем камеру
camera.position.z = clamp(camera.position.z + ZOOM_SPEED, MIN_ZOOM, MAX_ZOOM)
# 3. Вращение точки pivot мышью ПРИ ЗАЖАТОЙ ПКМ
if event is InputEventMouseMotion and Input.mouse_mode == Input.MOUSE_MODE_CAPTURED:
if camera_pivot:
camera_pivot.rotate_y(-event.relative.x * MOUSE_SENSITIVITY)
camera_pivot.rotate_object_local(Vector3.RIGHT, -event.relative.y * MOUSE_SENSITIVITY)
camera_pivot.rotation.x = clamp(camera_pivot.rotation.x, MIN_PITCH, MAX_PITCH)
func _physics_process(delta: float) -> void:
# Гравитация
if not is_on_floor():
velocity.y -= GRAVITY * delta
# 4. Полное WASD движение (Вперед/Назад и Влево/Вправо)
var input_dir := Vector2.ZERO
if Input.is_key_pressed(KEY_W) or Input.is_action_pressed("ui_up"):
input_dir.y -= 1.0
if Input.is_key_pressed(KEY_S) or Input.is_action_pressed("ui_down"):
input_dir.y += 1.0
if Input.is_key_pressed(KEY_A) or Input.is_action_pressed("ui_left"):
input_dir.x -= 1.0
if Input.is_key_pressed(KEY_D) or Input.is_action_pressed("ui_right"):
input_dir.x += 1.0
input_dir = input_dir.normalized()
# Движение рассчитывается относительно ориентации Player
var direction := (transform.basis * Vector3(input_dir.x, 0, input_dir.y)).normalized()
if direction != Vector3.ZERO:
velocity.x = direction.x * SPEED
velocity.z = direction.z * SPEED
else:
velocity.x = move_toward(velocity.x, 0, SPEED)
velocity.z = move_toward(velocity.z, 0, SPEED)
move_and_slide()
_update_sprite_texture()
func _update_sprite_texture() -> void:
if not sprite or not camera or not camera_pivot:
return
# Вычисляем угол поворота CameraPivot вокруг оси Y
# fmod_p позволяет обрезать угол в диапазон от -PI до PI
var pivot_y = fmod(camera_pivot.rotation.y, TAU)
if pivot_y > PI:
pivot_y -= TAU
elif pivot_y < -PI:
pivot_y += TAU
# Если точка обзора камеры повернулась на переднюю полусферу (угол больше 90° или меньше -90°)
if abs(pivot_y) > PI / 2.0:
if texture_front:
sprite.texture = texture_front
else:
if texture_back:
sprite.texture = texture_back