Files
CourierOld/Game/CourierOld/Assets/Code/Gameplay/Character/Controller/Abilities/WallLeanMechanic.cs
T
2025-11-10 22:16:36 +03:00

93 lines
3.5 KiB
C#

using UnityEngine;
using KinematicCharacterController;
namespace Code.Gameplay.Character.Controller.Abilities
{
/*
* Механика наклона персонажа при контакте со стеной
* Управляет визуальным наклоном модели в зависимости от высоты подъёма по стене
*/
public class WallLeanMechanic
{
private readonly float _maxLeanAngle;
private readonly float _leanSharpness;
private readonly float _heightScale;
private Transform _characterTransform;
private Vector3 _lastWallNormal;
private float _lastWallContactHeight;
private float _leanFactor;
private float _leanSign;
private Vector3 _leanDirection;
public WallLeanMechanic(float maxLeanAngle, float leanSharpness, float heightScale)
{
_maxLeanAngle = maxLeanAngle;
_leanSharpness = leanSharpness;
_heightScale = heightScale;
}
// Назначаем трансформ визуальной модели один раз
public void SetCharacterTransform(Transform transform)
{
_characterTransform = transform;
}
// Вызывается из OnMovementHit
public void OnWallContact(KinematicCharacterMotor motor, Vector3 wallNormal, float contactHeight)
{
_lastWallNormal = wallNormal;
_lastWallContactHeight = contactHeight;
_leanDirection = -_lastWallNormal;
_leanDirection = Vector3.ProjectOnPlane(_leanDirection, motor.CharacterUp).normalized;
_leanSign = Mathf.Sign(_lastWallNormal.x != 0
? Vector3.Dot(_leanDirection, motor.CharacterForward * _lastWallNormal.z)
: Vector3.Dot(_leanDirection, motor.CharacterForward * _lastWallNormal.z));
Debug.Log("CharacterUp: " + motor.CharacterUp);
Debug.Log("CharacterForward: " + motor.CharacterForward);
Debug.Log("CharacterRight: " + motor.CharacterRight);
Debug.Log(_leanSign);
}
// Обновление каждый кадр
public void UpdateLean(KinematicCharacterMotor motor, float deltaTime)
{
if (!_characterTransform) return;
var isGrounded = motor.GroundingStatus.IsStableOnGround;
if (!isGrounded)
{
var heightDelta = motor.TransientPosition.y - _lastWallContactHeight;
var targetLeanFactor = Mathf.Clamp01(heightDelta * _heightScale);
_leanFactor = Mathf.Lerp(_leanFactor, targetLeanFactor, 1f - Mathf.Exp(-_leanSharpness * deltaTime));
}
else
{
_leanFactor = Mathf.Lerp(_leanFactor, 0f, 1f - Mathf.Exp(-_leanSharpness * deltaTime));
}
ApplyLeanRotation(motor);
}
private void ApplyLeanRotation(KinematicCharacterMotor motor)
{
if (_leanFactor <= 0.01f)
{
_characterTransform.localRotation = Quaternion.identity;
return;
}
if (_leanDirection.sqrMagnitude < 0.01f) return;
var leanAngle = _leanSign * _leanFactor * _maxLeanAngle;
var leanRotation = Quaternion.AngleAxis(leanAngle, Vector3.Cross(motor.CharacterUp, _leanDirection));
_characterTransform.localRotation = leanRotation;
}
}
}