This commit is contained in:
Redsandy
2026-01-18 20:56:24 +03:00
commit 7980e0add8
68 changed files with 1605294 additions and 0 deletions

249
scripts/player.gd Normal file
View File

@@ -0,0 +1,249 @@
extends Unit
class_name Player
# Класс игрока с управлением WASD
@export var mouse_sensitivity: float = 0.003
@export var dash_speed: float = 25.0 # Скорость рывка
@export var dash_duration: float = 0.2 # Длительность рывка
@export var dash_cooldown: float = 1.0 # Перезарядка рывка
@export var straight_projectile_scene: PackedScene = null # Сцена прямого снаряда
@export var parabolic_projectile_scene: PackedScene = null # Сцена параболического снаряда
@export var projectile_damage: float = 20.0
@export var projectile_speed: float = 20.0
@export var explosion_radius: float = 3.0
var camera_angle: float = 0.0
var is_dashing: bool = false
var dash_timer: float = 0.0
var dash_cooldown_timer: float = 0.0
var dash_direction: Vector3 = Vector3.ZERO
signal dash_started
signal dash_ended
signal dash_cooldown_changed(remaining_time: float)
func _ready():
super._ready()
# Игрок белый
var mesh_instance = get_node_or_null("MeshInstance3D")
if mesh_instance:
var material = StandardMaterial3D.new()
material.albedo_color = Color.WHITE
mesh_instance.material_override = material
func _input(event):
# Обработка рывка
if event.is_action_pressed("dash") and dash_cooldown_timer <= 0.0 and not is_dashing:
perform_dash()
# Обработка стрельбы (левая кнопка мыши)
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
shoot_straight_projectile()
# Обработка броска (правая кнопка мыши)
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_RIGHT and event.pressed:
throw_parabolic_projectile()
func _physics_process(delta):
# Обновляем таймеры
var previous_cooldown = dash_cooldown_timer
if dash_cooldown_timer > 0.0:
dash_cooldown_timer -= delta
dash_cooldown_timer = max(0.0, dash_cooldown_timer)
# Эмитим сигнал только если значение изменилось
if abs(previous_cooldown - dash_cooldown_timer) > 0.01:
dash_cooldown_changed.emit(dash_cooldown_timer)
# Поворачиваем игрока в сторону курсора мыши
update_rotation_to_mouse()
# Получаем направление движения от клавиатуры
var input_dir = Input.get_vector("move_left", "move_right", "move_forward", "move_back")
var direction = Vector3(input_dir.x, 0, input_dir.y)
# Обработка рывка
if is_dashing:
dash_timer -= delta
if dash_timer <= 0.0:
end_dash()
else:
# Во время рывка двигаемся с увеличенной скоростью
velocity.x = dash_direction.x * dash_speed
velocity.z = dash_direction.z * dash_speed
else:
# Обычное движение
if direction.length() > 0:
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)
# Применяем гравитацию
if not is_on_floor():
velocity.y -= 9.8 * delta
else:
velocity.y = 0
super._physics_process(delta)
func update_rotation_to_mouse():
# Получаем камеру из сцены
var camera = get_viewport().get_camera_3d()
if not camera:
return
# Получаем позицию курсора мыши на экране
var mouse_pos = get_viewport().get_mouse_position()
# Создаем луч от камеры через позицию курсора
var from = camera.project_ray_origin(mouse_pos)
var to = from + camera.project_ray_normal(mouse_pos) * 1000.0
# Создаем запрос для raycast
var space_state = get_world_3d().direct_space_state
var query = PhysicsRayQueryParameters3D.create(from, to)
query.collision_mask = 1 # Слой земли
# Выполняем raycast
var result = space_state.intersect_ray(query)
if result:
# Получаем точку пересечения
var target_point = result.position
# Поворачиваем игрока в сторону курсора (только по оси Y)
var look_direction = (target_point - global_position)
look_direction.y = 0 # Игнорируем вертикальную составляющую
if look_direction.length() > 0.1:
look_at(global_position + look_direction, Vector3.UP)
func perform_dash():
# Получаем направление движения
var input_dir = Input.get_vector("move_left", "move_right", "move_forward", "move_back")
var direction = Vector3(input_dir.x, 0, input_dir.y)
# Если игрок не двигается, рывок вперед (по направлению взгляда или вверх)
if direction.length() < 0.1:
direction = Vector3(0, 0, -1) # Вперед по умолчанию
# Нормализуем направление
dash_direction = direction.normalized()
# Запускаем рывок
is_dashing = true
dash_timer = dash_duration
dash_cooldown_timer = dash_cooldown
dash_started.emit()
func end_dash():
is_dashing = false
dash_timer = 0.0
dash_ended.emit()
func shoot_straight_projectile():
if not straight_projectile_scene:
return
# Получаем точку прицеливания через raycast
var target_point = get_mouse_target_point()
if not target_point:
return
# Создаем снаряд
var projectile = straight_projectile_scene.instantiate() as StraightProjectile
if not projectile:
return
# Устанавливаем параметры
projectile.owner_unit = self
projectile.damage = projectile_damage
projectile.speed = projectile_speed
projectile.is_explosive = false
# Направление полета
var shoot_position = global_position + Vector3.UP * 1.0 # Немного выше игрока
var direction = (target_point - shoot_position).normalized()
projectile.direction = direction
# Позиция и поворот
projectile.global_position = shoot_position
projectile.look_at(shoot_position + direction, Vector3.UP)
# Добавляем в сцену
var scene_root = get_tree().current_scene
if not scene_root:
scene_root = get_tree().root.get_child(get_tree().root.get_child_count() - 1)
scene_root.add_child(projectile)
func throw_parabolic_projectile():
if not parabolic_projectile_scene:
# Пробуем загрузить вручную
parabolic_projectile_scene = load("res://scenes/parabolic_projectile.tscn") as PackedScene
if not parabolic_projectile_scene:
return
# Получаем точку прицеливания через raycast (на плоскости)
var target_point = get_mouse_target_point()
# Убеждаемся, что целевая точка на уровне земли (Y = 0)
# Если кликнули на противника или другой объект, используем его X и Z, но Y ставим на землю
target_point.y = 0.0
# Позиция запуска
var throw_position = global_position + Vector3.UP * 1.0
# Создаем снаряд
var projectile = parabolic_projectile_scene.instantiate()
if not projectile:
return
# Устанавливаем параметры ДО добавления в сцену
projectile.owner_unit = self
projectile.damage = projectile_damage
projectile.speed = projectile_speed
projectile.is_explosive = true
projectile.explosion_radius = explosion_radius
# Устанавливаем позицию
projectile.global_position = throw_position
# Целевая позиция (на уровне земли) - устанавливаем ДО _ready()
projectile.target_position = target_point
# Добавляем в сцену (после установки всех параметров)
var scene_root = get_tree().current_scene
if not scene_root:
scene_root = get_tree().root.get_child(get_tree().root.get_child_count() - 1)
scene_root.add_child(projectile)
func get_mouse_target_point() -> Vector3:
# Получаем камеру
var camera = get_viewport().get_camera_3d()
if not camera:
return global_position + transform.basis.z * -10.0
# Получаем позицию курсора мыши
var mouse_pos = get_viewport().get_mouse_position()
# Создаем луч от камеры
var from = camera.project_ray_origin(mouse_pos)
var ray_dir = camera.project_ray_normal(mouse_pos)
var to = from + ray_dir * 1000.0
# Raycast
var space_state = get_world_3d().direct_space_state
var query = PhysicsRayQueryParameters3D.create(from, to)
query.collision_mask = 1
var result = space_state.intersect_ray(query)
if result:
return result.position
# Если не попали ни во что, вычисляем точку на плоскости земли
if ray_dir.y < -0.01: # Луч направлен вниз
var t = -from.y / ray_dir.y
return from + ray_dir * t
# Если луч не направлен вниз, используем точку перед игроком
return global_position + transform.basis.z * -10.0