инишиал титан падать второй мувмент (но пока очень говно)
This commit is contained in:
1 parent
7130513471
commit
b271042137
7 files changed
+230
-1024
No files matched your search
+158
@@ -0,0 +1,158 @@
|
||||
using Code.Gameplay.Character.Controller.Input;
|
||||
using Code.Gameplay.Character.Core;
|
||||
using KinematicCharacterController;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Code.Gameplay.Character.Controller.Abilities
|
||||
{
|
||||
public class WallRunningMechanic
|
||||
{
|
||||
public float MaxWallRunTime = 1.75f;
|
||||
public float WallRunSpeed = 8.0f;
|
||||
public float WallRunAcceleration = 25.0f;
|
||||
public float WallRunGravityScale = 0.25f;
|
||||
public float WallDetachImpulse = 7.0f;
|
||||
public float WallContactAngleThreshold = 45.0f;
|
||||
|
||||
private bool _isWallRunning;
|
||||
private float _wallRunTimer;
|
||||
private Vector3 _wallNormal;
|
||||
private Vector3 _wallTangentRight;
|
||||
private Vector3 _wallTangentUp;
|
||||
private Vector3 _wallContactPoint;
|
||||
private Vector3 _lastMoveInputInWallSpace;
|
||||
|
||||
private float _surfaceAngleCos;
|
||||
private bool _canWallRun;
|
||||
|
||||
private KinematicCharacterMotor _motor;
|
||||
private CharacterInputHandler _inputHandler;
|
||||
|
||||
public bool IsWallRunning => _isWallRunning;
|
||||
|
||||
public void Initialize(KinematicCharacterMotor motor, CharacterInputHandler inputHandler)
|
||||
{
|
||||
_motor = motor;
|
||||
_inputHandler = inputHandler;
|
||||
_surfaceAngleCos = Mathf.Cos(Mathf.Deg2Rad * WallContactAngleThreshold);
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_isWallRunning = false;
|
||||
_wallRunTimer = 0f;
|
||||
_wallNormal = Vector3.zero;
|
||||
}
|
||||
|
||||
public void Tick(float deltaTime)
|
||||
{
|
||||
if (_isWallRunning)
|
||||
{
|
||||
_wallRunTimer += deltaTime;
|
||||
if (_wallRunTimer > MaxWallRunTime)
|
||||
{
|
||||
DetachFromWall(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryStartWallRun(Vector3 hitNormal, Vector3 hitPoint)
|
||||
{
|
||||
if (_isWallRunning) return false;
|
||||
|
||||
float dotWithUp = Vector3.Dot(hitNormal, _motor.CharacterUp);
|
||||
bool isWall = Mathf.Abs(dotWithUp) < _surfaceAngleCos;
|
||||
|
||||
if (!isWall) return false;
|
||||
|
||||
if (_motor.GroundingStatus.IsStableOnGround) return false;
|
||||
|
||||
float velocityIntoWall = Vector3.Dot(_motor.Velocity, -hitNormal);
|
||||
if (velocityIntoWall < 1f) return false;
|
||||
|
||||
StartWallRun(hitNormal, hitPoint);
|
||||
return true;
|
||||
}
|
||||
|
||||
private void StartWallRun(Vector3 wallNormal, Vector3 contactPoint)
|
||||
{
|
||||
_isWallRunning = true;
|
||||
_wallRunTimer = 0f;
|
||||
_wallNormal = wallNormal.normalized;
|
||||
_wallContactPoint = contactPoint;
|
||||
|
||||
_wallTangentUp = Vector3.ProjectOnPlane(_motor.CharacterUp, _wallNormal).normalized;
|
||||
if (_wallTangentUp.sqrMagnitude < 0.01f) _wallTangentUp = Vector3.up;
|
||||
|
||||
_wallTangentRight = Vector3.Cross(_wallNormal, _wallTangentUp).normalized;
|
||||
|
||||
_motor.ForceUnground();
|
||||
}
|
||||
|
||||
public void HandleWallRun(ref Vector3 currentVelocity, CharacterInputs inputs, float deltaTime)
|
||||
{
|
||||
if (!_isWallRunning) return;
|
||||
|
||||
Vector3 moveInputWorld = _inputHandler.MoveInputVector;
|
||||
|
||||
Vector3 moveInWallPlane = Vector3.ProjectOnPlane(moveInputWorld, _wallNormal).normalized;
|
||||
|
||||
float inputMagnitude = moveInputWorld.magnitude;
|
||||
moveInWallPlane *= inputMagnitude;
|
||||
|
||||
Vector3 targetVelocity = moveInWallPlane * WallRunSpeed;
|
||||
|
||||
currentVelocity = Vector3.Lerp(
|
||||
currentVelocity,
|
||||
targetVelocity,
|
||||
1f - Mathf.Exp(-WallRunAcceleration * deltaTime)
|
||||
);
|
||||
|
||||
Vector3 gravity = Physics.gravity * WallRunGravityScale;
|
||||
currentVelocity += gravity * deltaTime;
|
||||
|
||||
float distanceToWall = Vector3.Dot(_motor.TransientPosition - _wallContactPoint, _wallNormal);
|
||||
if (Mathf.Abs(distanceToWall) > 0.8f)
|
||||
{
|
||||
DetachFromWall(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (inputs.JumpPressed)
|
||||
{
|
||||
DetachFromWall(true);
|
||||
}
|
||||
}
|
||||
|
||||
public void DetachFromWall(bool withImpulse)
|
||||
{
|
||||
if (!_isWallRunning) return;
|
||||
|
||||
_isWallRunning = false;
|
||||
|
||||
if (withImpulse && _motor != null)
|
||||
{
|
||||
Vector3 detachImpulse = -_wallNormal * WallDetachImpulse;
|
||||
_motor.BaseVelocity += detachImpulse;
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3 GetWallNormal() => _wallNormal;
|
||||
public bool TryGetWallTangents(out Vector3 up, out Vector3 right)
|
||||
{
|
||||
if (!_isWallRunning)
|
||||
{
|
||||
up = right = Vector3.zero;
|
||||
return false;
|
||||
}
|
||||
up = _wallTangentUp;
|
||||
right = _wallTangentRight;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool CanWallRun()
|
||||
{
|
||||
return !_isWallRunning && !_motor.GroundingStatus.IsStableOnGround;
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4bef67aa9cf14753a63ea07eecc5ff67
|
||||
timeCreated: 1764263348
|
||||
+48
-83
@@ -15,7 +15,7 @@ namespace Code.Gameplay.Character.Controller
|
||||
* Реализует физическое движение персонажа с использованием Kinematic Character Controller
|
||||
* Обрабатывает ввод пользователя, применяет движение и вращение с учетом поверхности
|
||||
*
|
||||
* Источник: Tvtig, https://www.youtube.com/watch?v=9ibpY64wBYM
|
||||
* Источник: Tvtig, https://www.youtube.com/watch?v=9ibpY64wBYM
|
||||
*/
|
||||
public class CustomCharacterController : MonoBehaviour, ICharacterController
|
||||
{
|
||||
@@ -28,49 +28,23 @@ namespace Code.Gameplay.Character.Controller
|
||||
// Подсистемы
|
||||
private CharacterInputHandler _inputHandler;
|
||||
private SlideMechanic _slideMechanic;
|
||||
private WallClimbMechanic _wallClimbMechanic;
|
||||
// private WallClimbMechanic _wallClimbMechanic;
|
||||
private WallRunningMechanic _wallRunningMechanic;
|
||||
private WallLeanMechanic _wallLeanMechanic;
|
||||
private GroundMovementSystem _groundMovement;
|
||||
private AirMovementSystem _airMovement;
|
||||
|
||||
public bool IsSliding => _slideMechanic.IsSliding;
|
||||
public bool IsWallRunning => _wallRunningMechanic.IsWallRunning;
|
||||
|
||||
private float abilityBlockTimer;
|
||||
private bool isAbilitiesBlocked;
|
||||
|
||||
private CharacterInputs _lastInputs;
|
||||
|
||||
public void SetPosition(Vector3 position) => motor.SetPosition(position);
|
||||
public void SetRotation(Quaternion rotation) => motor.SetRotation(rotation);
|
||||
|
||||
// Инициализация контроллера
|
||||
// private void Awake()
|
||||
// {
|
||||
// // Инициализация подсистем с параметрами из конфига
|
||||
// _inputHandler = new CharacterInputHandler(
|
||||
// _characterConfig.sprintAngleThreshold
|
||||
// );
|
||||
// _slideMechanic = new SlideMechanic(
|
||||
// _characterConfig.slideSpeed,
|
||||
// _characterConfig.slideDuration,
|
||||
// _characterConfig.slideCooldown
|
||||
// );
|
||||
// _wallClimbMechanic = new WallClimbMechanic(
|
||||
// _characterConfig.wallClimbSpeedFactor,
|
||||
// _characterConfig.minWallClimbSpeed,
|
||||
// _characterConfig.wallDetectionAngleThreshold
|
||||
// );
|
||||
// _wallLeanMechanic = new WallLeanMechanic(
|
||||
// _characterConfig.maxWallLeanAngle,
|
||||
// _characterConfig.wallLeanSharpness,
|
||||
// _characterConfig.wallLeanHeightScale
|
||||
// );
|
||||
// _groundMovement = new GroundMovementSystem(
|
||||
// _characterConfig.maxStableMoveSpeed,
|
||||
// _characterConfig.sprintMoveSpeed,
|
||||
// _characterConfig.stableMovementSharpness,
|
||||
// _characterConfig.jumpSpeed
|
||||
// );
|
||||
// _airMovement = new AirMovementSystem(_characterConfig.gravity);
|
||||
// }
|
||||
[Inject]
|
||||
private void Construct(CharacterConfig characterConfig)
|
||||
{
|
||||
@@ -84,11 +58,20 @@ namespace Code.Gameplay.Character.Controller
|
||||
_characterConfig.slideDuration,
|
||||
_characterConfig.slideCooldown
|
||||
);
|
||||
_wallClimbMechanic = new WallClimbMechanic(
|
||||
_characterConfig.wallClimbSpeedFactor,
|
||||
_characterConfig.minWallClimbSpeed,
|
||||
_characterConfig.wallDetectionAngleThreshold
|
||||
);
|
||||
// _wallClimbMechanic = new WallClimbMechanic(
|
||||
// _characterConfig.wallClimbSpeedFactor,
|
||||
// _characterConfig.minWallClimbSpeed,
|
||||
// _characterConfig.wallDetectionAngleThreshold
|
||||
// );
|
||||
_wallRunningMechanic = new WallRunningMechanic();
|
||||
// Настройка параметров WallRunningMechanic (можно вынести в конфиг позже)
|
||||
_wallRunningMechanic.MaxWallRunTime = 1.75f;
|
||||
_wallRunningMechanic.WallRunSpeed = 8.0f;
|
||||
_wallRunningMechanic.WallRunAcceleration = 25.0f;
|
||||
_wallRunningMechanic.WallRunGravityScale = 0.25f;
|
||||
_wallRunningMechanic.WallDetachImpulse = 7.0f;
|
||||
_wallRunningMechanic.WallContactAngleThreshold = 45.0f;
|
||||
|
||||
_wallLeanMechanic = new WallLeanMechanic(
|
||||
_characterConfig.maxWallLeanAngle,
|
||||
_characterConfig.wallLeanSharpness,
|
||||
@@ -107,30 +90,23 @@ namespace Code.Gameplay.Character.Controller
|
||||
_inputHandler.FirstPersonCameraRotation =
|
||||
motor.Transform.rotation; // Начальное вращение совпадает с персонажемс
|
||||
_wallLeanMechanic.SetCharacterTransform(characterTransform);
|
||||
_wallRunningMechanic.Initialize(motor, _inputHandler);
|
||||
}
|
||||
|
||||
// Текущий режим камеры для адаптации поведения
|
||||
private CameraMode _currentCameraMode = CameraMode.FirstPerson;
|
||||
|
||||
// Инициализация контроллера
|
||||
// private void Start()
|
||||
// {
|
||||
// motor.CharacterController = this; // Регистрация этого контроллера в движке
|
||||
// _inputHandler.FirstPersonCameraRotation =
|
||||
// motor.Transform.rotation; // Начальное вращение совпадает с персонажемс
|
||||
// _wallLeanMechanic.SetCharacterTransform(characterTransform);
|
||||
// }
|
||||
|
||||
// Обработка и преобразование входных данных от персонажа
|
||||
public void SetInputs(ref CharacterInputs inputs)
|
||||
{
|
||||
// Обновляем таймер перезарядки слайда
|
||||
_lastInputs = inputs;
|
||||
// Обновляем таймеры механик
|
||||
_slideMechanic.TickCooldown(Time.deltaTime);
|
||||
_wallClimbMechanic.TickCooldown(Time.deltaTime);
|
||||
// _wallClimbMechanic.TickCooldown(Time.deltaTime);
|
||||
_wallRunningMechanic.Tick(Time.deltaTime);
|
||||
|
||||
// Если активен слайд - игнорируем весь остальной ввод
|
||||
if (_slideMechanic.IsSliding)
|
||||
{
|
||||
// Во время слайда не обрабатываем движение, прыжки и спринт
|
||||
_inputHandler.MoveInputVector = Vector3.zero;
|
||||
_inputHandler.LookInputVector = Vector3.one;
|
||||
_inputHandler.IsSprinting = false;
|
||||
@@ -138,7 +114,6 @@ namespace Code.Gameplay.Character.Controller
|
||||
return;
|
||||
}
|
||||
|
||||
// Сохраняем текущий режим камеры
|
||||
_currentCameraMode = inputs.CameraMode;
|
||||
|
||||
if (isAbilitiesBlocked)
|
||||
@@ -161,42 +136,33 @@ namespace Code.Gameplay.Character.Controller
|
||||
// Обработка остального ввода
|
||||
_inputHandler.ProcessInputs(ref inputs, motor);
|
||||
|
||||
// Передаём запрос прыжка напрямую в систему движения по земле
|
||||
// Передаём запрос прыжка
|
||||
_groundMovement.RequestJump(inputs.JumpPressed);
|
||||
}
|
||||
|
||||
// Обновление вращения персонажа на основе входных данных
|
||||
public void UpdateRotation(ref Quaternion currentRotation, float deltaTime)
|
||||
{
|
||||
if (_slideMechanic.IsSliding || _wallClimbMechanic.IsClimbingActive) return;
|
||||
if (_slideMechanic.IsSliding || _wallRunningMechanic.IsWallRunning) return;
|
||||
|
||||
switch (_currentCameraMode)
|
||||
{
|
||||
// В FirstPerson режиме применяем вращение камеры напрямую к персонажу (только горизонталь)
|
||||
case CameraMode.FirstPerson when _inputHandler.ShouldApplyCameraRotation:
|
||||
// Немедленно применяем горизонтальное вращение камеры к персонажу
|
||||
currentRotation = _inputHandler.FirstPersonCameraRotation;
|
||||
break;
|
||||
// В ThirdPerson режиме плавно поворачиваем персонаж в направлении движения
|
||||
case CameraMode.ThirdPerson:
|
||||
{
|
||||
// Вращение применяется только если есть направление движения и разрешена резкость вращения
|
||||
if (_inputHandler.LookInputVector.sqrMagnitude > 0 && _characterConfig.orientationSharpness > 0)
|
||||
{
|
||||
// Плавная интерполяция текущего направления к целевому с экспоненциальным сглаживанием
|
||||
var smoothedLookInputDirection = Vector3.Slerp(
|
||||
motor.CharacterForward,
|
||||
_inputHandler.LookInputVector,
|
||||
1f - Mathf.Exp(-_characterConfig.orientationSharpness * deltaTime)
|
||||
).normalized;
|
||||
|
||||
// Установка нового вращения смотря в сглаженном направлении
|
||||
currentRotation = Quaternion.LookRotation(smoothedLookInputDirection, motor.CharacterUp);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
// Заготовка на будущее (если понадобится)
|
||||
case CameraMode.Static:
|
||||
case CameraMode.Cinematic:
|
||||
default:
|
||||
@@ -204,38 +170,40 @@ namespace Code.Gameplay.Character.Controller
|
||||
}
|
||||
}
|
||||
|
||||
// Обновление скорости движения персонажа
|
||||
public void UpdateVelocity(ref Vector3 currentVelocity, float deltaTime)
|
||||
{
|
||||
// Если во время слайда запрошен прыжок — прерываем слайд
|
||||
if (_slideMechanic.IsSliding && _groundMovement.JumpRequested)
|
||||
{
|
||||
_slideMechanic.EndSlide();
|
||||
}
|
||||
|
||||
// Проверяем запрос слайда и запускаем его если можно
|
||||
if (!_slideMechanic.IsSliding)
|
||||
{
|
||||
_slideMechanic.TryStartSlide(motor, _currentCameraMode, _inputHandler.FirstPersonCameraRotation,
|
||||
_inputHandler.LookInputVector);
|
||||
}
|
||||
// Если активен слайд - обрабатываем отдельно
|
||||
else
|
||||
{
|
||||
_slideMechanic.HandleSlide(ref currentVelocity, deltaTime, motor);
|
||||
return;
|
||||
}
|
||||
|
||||
// === WALL RUNNING: обработка состояния ===
|
||||
if (_wallRunningMechanic.IsWallRunning)
|
||||
{
|
||||
_wallRunningMechanic.HandleWallRun(ref currentVelocity, _lastInputs, deltaTime);
|
||||
return;
|
||||
}
|
||||
|
||||
// Если персонаж находится на земле
|
||||
if (motor.GroundingStatus.IsStableOnGround)
|
||||
{
|
||||
_groundMovement.UpdateVelocity(ref currentVelocity, _inputHandler, motor, deltaTime);
|
||||
}
|
||||
// Если персонаж не находится на земле
|
||||
// Если персонаж не на земле — стандартное воздушное движение
|
||||
else
|
||||
{
|
||||
_airMovement.UpdateVelocity(ref currentVelocity, deltaTime);
|
||||
// Отменяем прыжок (фикс проблемы с двойным прыжком после приземления)
|
||||
_groundMovement.CancelJumpRequest();
|
||||
}
|
||||
}
|
||||
@@ -246,60 +214,57 @@ namespace Code.Gameplay.Character.Controller
|
||||
abilityBlockTimer = duration;
|
||||
}
|
||||
|
||||
// Метод вызывается перед обновлением персонажа (можно использовать для подготовки)
|
||||
public void BeforeCharacterUpdate(float deltaTime)
|
||||
{
|
||||
}
|
||||
|
||||
// Метод вызывается после обработки столкновения с землей
|
||||
public void PostGroundingUpdate(float deltaTime)
|
||||
{
|
||||
// Если во время слайда персонаж оторвался от земли - заканчиваем слайд
|
||||
if (_slideMechanic.IsSliding && !motor.GroundingStatus.IsStableOnGround)
|
||||
{
|
||||
_slideMechanic.EndSlide();
|
||||
}
|
||||
|
||||
if (motor.GroundingStatus.IsStableOnGround)
|
||||
{
|
||||
_wallClimbMechanic.ResetWallClimbSession();
|
||||
}
|
||||
// if (motor.GroundingStatus.IsStableOnGround)
|
||||
// {
|
||||
// _wallClimbMechanic.ResetWallClimbSession();
|
||||
// }
|
||||
}
|
||||
|
||||
// Метод вызывается после обновления персонажа
|
||||
public void AfterCharacterUpdate(float deltaTime)
|
||||
{
|
||||
_wallLeanMechanic.UpdateLean(motor, deltaTime);
|
||||
}
|
||||
|
||||
// Проверка валидности коллайдера для обработки столкновений
|
||||
public bool IsColliderValidForCollisions(Collider coll)
|
||||
{
|
||||
return true; // Все коллайдеры считаются валидными для столкновений
|
||||
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)
|
||||
{
|
||||
if (_wallClimbMechanic.TryClimbWall(motor, hitNormal))
|
||||
// Попытка начать Wall Run при ударе о поверхность
|
||||
if (_wallRunningMechanic.TryStartWallRun(hitNormal, hitPoint))
|
||||
{
|
||||
_wallLeanMechanic.OnWallContact(motor, hitNormal, motor.TransientPosition.y);
|
||||
}
|
||||
|
||||
// if (_wallClimbMechanic.TryClimbWall(motor, hitNormal))
|
||||
// _wallLeanMechanic.OnWallContact(motor, hitNormal, motor.TransientPosition.y);
|
||||
}
|
||||
|
||||
// Обработка отчета о стабильности столкновения
|
||||
public void ProcessHitStabilityReport(Collider hitCollider, Vector3 hitNormal, Vector3 hitPoint,
|
||||
Vector3 atCharacterPosition,
|
||||
Quaternion atCharacterRotation, ref HitStabilityReport hitStabilityReport)
|
||||
{
|
||||
}
|
||||
|
||||
// Обработка дискретных столкновений (для триггеров и специальных коллайдеров)
|
||||
public void OnDiscreteCollisionDetected(Collider hitCollider)
|
||||
{
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user