Добавлено базовое перемещение, тестовый материал

This commit is contained in:
MrTorex committed 2025-10-16 23:18:22 +03:00
1 parent 86c9c126a6
commit 8981457f2d
35 files changed
+1052 -6

No files matched your search

@@ -0,0 +1,157 @@
using KinematicCharacterController;
using UnityEngine;
namespace Code.Gameplay.Player
{
/*
* Скрипт, отвечающий за передвижения игрока
* Реализует физическое движение персонажа с использованием Kinematic Character Controller
* Обрабатывает ввод пользователя, применяет движение и вращение с учетом поверхности
*
* Источник: Tvtig, https://www.youtube.com/watch?v=9ibpY64wBYM
*/
// Структура для хранения входных данных от игрока
public struct PlayerInputs
{
public float MoveAxisForward; // Ввод движения вперед/назад (W/S или Up/Down)
public float MoveAxisRight; // Ввод движения влево/вправо (A/D или Left/Right)
public Quaternion CameraRotation; // Текущее вращение камеры для определения направления движения
}
public class CharacterController : MonoBehaviour, ICharacterController
{
[SerializeField]
private KinematicCharacterMotor motor; // Движок физического перемещения персонажа
[SerializeField]
private float
maxStableMoveSpeed = 10f, // Максимальная скорость движения по устойчивой поверхности
stableMovementSharpness = 15f, // Резкость изменения скорости движения (чем выше - тем резче)
orientationSharpness = 10f; // Резкость изменения вращения персонажа (чем выше - тем резче)
// Векторы движения и направления взгляда
private Vector3 _moveInputVector, _lookInputVector;
// Инициализация контроллера
private void Start()
{
motor.CharacterController = this; // Регистрация этого контроллера в движке
}
// Обработка и преобразование входных данных от игрока
public void SetInputs(ref PlayerInputs inputs)
{
// Нормализация вектора движения и ограничение его величины до 1
Vector3 moveInputVector =
Vector3.ClampMagnitude(new Vector3(inputs.MoveAxisRight, 0, inputs.MoveAxisForward), 1f);
// Получение направления камеры в горизонтальной плоскости (игнорируя наклон)
Vector3 cameraPlanarDirection =
Vector3.ProjectOnPlane(inputs.CameraRotation * Vector3.forward, motor.CharacterUp).normalized;
// Резервный расчет направления, если основное направление параллельно вертикали
if (cameraPlanarDirection.sqrMagnitude == 0f)
{
cameraPlanarDirection = Vector3.ProjectOnPlane(inputs.CameraRotation * Vector3.up, motor.CharacterUp);
}
// Создание вращения на основе направления камеры
Quaternion cameraPlanarRotation = Quaternion.LookRotation(cameraPlanarDirection, motor.CharacterUp);
// Преобразование локального ввода в мировые координаты относительно камеры
_moveInputVector = cameraPlanarRotation * moveInputVector;
// Направление взгляда совпадает с направлением движения (нормализованное)
_lookInputVector = _moveInputVector.normalized;
}
// Обновление вращения персонажа на основе входных данных
public void UpdateRotation(ref Quaternion currentRotation, float deltaTime)
{
// Вращение применяется только если есть направление движения и разрешена резкость вращения
if (_lookInputVector.sqrMagnitude > 0 && orientationSharpness > 0)
{
// Плавная интерполяция текущего направления к целевому с экспоненциальным сглаживанием
Vector3 smoothedLookInputDirection = Vector3.Slerp(
motor.CharacterForward,
_lookInputVector,
1f - Mathf.Exp(-orientationSharpness * deltaTime)
).normalized;
// Установка нового вращения смотря в сглаженном направлении
currentRotation = Quaternion.LookRotation(smoothedLookInputDirection, motor.CharacterUp);
}
}
// Обновление скорости движения персонажа
public void UpdateVelocity(ref Vector3 currentVelocity, float deltaTime)
{
// Сохранение текущей величины скорости
float currentVelocityMagnitude = currentVelocity.magnitude;
// Получение нормали поверхности для корректного движения по наклонным плоскостям
Vector3 effectiveGroundNormal = motor.GroundingStatus.GroundNormal;
// Переориентация текущей скорости вдоль поверхности
currentVelocity = motor.GetDirectionTangentToSurface(currentVelocity, effectiveGroundNormal) * currentVelocityMagnitude;
// Переориентация входного вектора движения вдоль поверхности
Vector3 inputRight = Vector3.Cross(_moveInputVector, motor.CharacterUp);
Vector3 reorientedInput = Vector3.Cross(effectiveGroundNormal, inputRight).normalized * _moveInputVector.magnitude;
// Расчет целевой скорости движения с учетом ограничения максимальной скорости
Vector3 targetMovementVelocity = reorientedInput * maxStableMoveSpeed;
// Плавная интерполяция текущей скорости к целевой с экспоненциальным сглаживанием
currentVelocity = Vector3.Lerp(
currentVelocity,
targetMovementVelocity,
1f - Mathf.Exp(-stableMovementSharpness * deltaTime)
);
}
// Метод вызывается перед обновлением персонажа (можно использовать для подготовки)
public void BeforeCharacterUpdate(float deltaTime)
{
}
// Метод вызывается после обработки столкновения с землей
public void PostGroundingUpdate(float deltaTime)
{
}
// Метод вызывается после обновления персонажа (можно использовать для пост-обработки)
public void AfterCharacterUpdate(float deltaTime)
{
}
// Проверка валидности коллайдера для обработки столкновений
public bool IsColliderValidForCollisions(Collider coll)
{
return true; // Все коллайдеры считаются валидными для столкновений
}
// Обработка столкновения с землей (вызывается при приземлении)
public void OnGroundHit(Collider hitCollider, Vector3 hitNormal, Vector3 hitPoint, ref HitStabilityReport hitStabilityReport)
{
}
// Обработка столкновения при движении (вызывается при ударе о препятствие)
public void OnMovementHit(Collider hitCollider, Vector3 hitNormal, Vector3 hitPoint,
ref HitStabilityReport hitStabilityReport)
{
}
// Обработка отчета о стабильности столкновения
public void ProcessHitStabilityReport(Collider hitCollider, Vector3 hitNormal, Vector3 hitPoint, Vector3 atCharacterPosition,
Quaternion atCharacterRotation, ref HitStabilityReport hitStabilityReport)
{
}
// Обработка дискретных столкновений (для триггеров и специальных коллайдеров)
public void OnDiscreteCollisionDetected(Collider hitCollider)
{
}
}
}
@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: a6f74123648343c498c883d02909b499
timeCreated: 1760641740
@@ -2,12 +2,15 @@ using Code.Gameplay.Player;
using Code.Input;
using Reflex.Attributes;
using UnityEngine;
using CharacterController = Code.Gameplay.Player.CharacterController;
public class Player : MonoBehaviour
{
[SerializeField]
private PlayerCamera playerCamera;
[SerializeField]
private CharacterController characterController;
[SerializeField]
private Transform cameraFollowPoint;
[Inject] private IInputService _inputService;
@@ -27,6 +30,24 @@ public class Player : MonoBehaviour
new Vector3(lookInput.x, lookInput.y, 0));
}
private void HandleCharacterInput()
{
PlayerInputs inputs = new PlayerInputs();
Vector2 moveInput = _inputService.MoveInput;
inputs.MoveAxisForward = moveInput.y;
inputs.MoveAxisRight = moveInput.x;
inputs.CameraRotation = playerCamera.transform.rotation;
characterController.SetInputs(ref inputs);
}
private void Update()
{
HandleCharacterInput();
}
private void LateUpdate()
{
HandleCameraInput();