Я ненавижу наклоны (не прорвёмся)

This commit is contained in:
MrTorex committed 2025-11-28 00:51:20 +03:00
1 parent d10d2cdca6
commit 766609489d
7 files changed
+97 -1463

No files matched your search

@@ -1,147 +1,59 @@
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;
private Vector3 _lastWallContactPoint;
private bool _isLeaningOnWall;
private const float WallProximityThreshold = 0.6f;
private Transform _model;
public WallLeanMechanic(float maxLeanAngle, float leanSharpness, float heightScale)
public void SetModel(Transform model) => _model = model;
public void Update(bool isWallRunning, Vector3 wallNormal, Vector3 characterForward)
{
_maxLeanAngle = maxLeanAngle;
_leanSharpness = leanSharpness;
_heightScale = heightScale;
}
// Назначаем трансформ визуальной модели один раз
public void SetCharacterTransform(Transform transform)
{
_characterTransform = transform;
}
// Вызывается из OnMovementHit
public void OnWallContact(KinematicCharacterMotor motor, Vector3 wallNormal, float contactHeight)
{
// Только если это действительно стена (не пол/потолок)
var dotWithUp = Vector3.Dot(wallNormal, motor.CharacterUp);
if (Mathf.Abs(dotWithUp) > 0.7f) // ~45°, можно настроить
return;
_lastWallNormal = wallNormal;
_lastWallContactHeight = contactHeight;
_lastWallContactPoint = motor.TransientPosition;
// Пересчитываем направление наклона
_leanDirection = -_lastWallNormal;
_leanDirection = Vector3.ProjectOnPlane(_leanDirection, motor.CharacterUp).normalized;
_leanSign = 1;
// what the fuck
if (_leanDirection.x != 0)
if (_model == null)
{
switch (_leanDirection.x)
{
case -1 or 1 when motor.CharacterRight.x > 0.1f:
_leanSign = -1;
break;
case -1 or 1 when Mathf.Abs(motor.CharacterRight.x) < 0.1f:
_leanDirection = new Vector3(0, 0, -1);
break;
}
}
else
{
switch (_leanDirection.z)
{
case 1 when Mathf.Abs(motor.CharacterRight.z) < 0.1f:
_leanSign = -1;
break;
case -1 when motor.CharacterRight.z < -0.1f:
case 1 when motor.CharacterRight.z > 0.1f:
_leanDirection = new Vector3(-1, 0, 0);
break;
case -1 when motor.CharacterRight.z > 0.1f:
case 1 when motor.CharacterRight.z < -0.1f:
_leanDirection = new Vector3(1, 0, 0);
break;
}
}
_isLeaningOnWall = true;
}
// Обновление каждый кадр
public void UpdateLean(KinematicCharacterMotor motor, float deltaTime)
{
if (!_characterTransform) return;
var currentPosition = motor.TransientPosition;
var isStillNearWall = false;
if (_isLeaningOnWall && _lastWallNormal != Vector3.zero)
{
var toWall = currentPosition - _lastWallContactPoint;
var distanceToWallPlane = Mathf.Abs(Vector3.Dot(toWall, _lastWallNormal));
isStillNearWall = distanceToWallPlane <= WallProximityThreshold;
}
if (_isLeaningOnWall)
{
var isBelowContactHeight = currentPosition.y < _lastWallContactHeight;
if (!isStillNearWall || isBelowContactHeight)
{
_isLeaningOnWall = false;
}
else
{
var heightDelta = currentPosition.y - _lastWallContactHeight;
var targetLeanFactor = Mathf.Clamp01(heightDelta * _heightScale);
_leanFactor = Mathf.Lerp(_leanFactor, targetLeanFactor, 1f - Mathf.Exp(-_leanSharpness * deltaTime));
}
}
if (!_isLeaningOnWall)
{
_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;
_model.localRotation = Quaternion.identity;
return;
}
if (_leanDirection.sqrMagnitude < 0.01f) return;
if (!isWallRunning)
{
_model.localRotation = Quaternion.identity;
return;
}
var leanAngle = _leanSign * _leanFactor * _maxLeanAngle;
var leanAxis = Vector3.Cross(motor.CharacterUp, _leanDirection);
var leanRotation = Quaternion.AngleAxis(leanAngle, leanAxis);
_characterTransform.localRotation = leanRotation;
Vector3 n = new Vector3(wallNormal.x, 0f, wallNormal.z).normalized;
if (n.sqrMagnitude < 0.001f) n = Vector3.forward;
Vector3 bodyRight = Vector3.Cross(Vector3.up, characterForward).normalized;
float side = Vector3.Dot(n, bodyRight);
float front = Vector3.Dot(n, characterForward);
Vector3 axis = Vector3.right;
float angle = 0f;
if (side > 0.3f)
{
axis = Vector3.forward;
angle = -25f;
}
else if (side < -0.3f)
{
axis = Vector3.forward;
angle = 25f;
}
else if (front > 0.3f)
{
axis = Vector3.right;
angle = -25f;
}
else if (front < -0.3f)
{
axis = Vector3.right;
angle = 25f;
}
_model.localRotation = Quaternion.AngleAxis(angle, axis);
}
}
}
@@ -24,6 +24,9 @@ namespace Code.Gameplay.Character.Controller.Abilities
private float _surfaceAngleCos;
private bool _canWallRun;
public float WallContactHeight { get; private set; }
public Vector3 WallRunStartForward { get; private set; }
private KinematicCharacterMotor _motor;
private CharacterInputHandler _inputHandler;
@@ -80,12 +83,14 @@ namespace Code.Gameplay.Character.Controller.Abilities
_wallRunTimer = 0f;
_wallNormal = wallNormal.normalized;
_wallContactPoint = contactPoint;
WallContactHeight = contactPoint.y;
_wallTangentUp = Vector3.ProjectOnPlane(_motor.CharacterUp, _wallNormal).normalized;
if (_wallTangentUp.sqrMagnitude < 0.01f) _wallTangentUp = Vector3.up;
_wallTangentRight = Vector3.Cross(_wallNormal, _wallTangentUp).normalized;
WallRunStartForward = _motor.CharacterForward;
_motor.ForceUnground();
}
@@ -220,8 +225,8 @@ namespace Code.Gameplay.Character.Controller.Abilities
if (forwardInWallPlane.sqrMagnitude < 0.01f) forwardInWallPlane = _wallTangentRight;
Vector3 jumpDirection =
forwardInWallPlane * 0.1f +
wallAway * 0.5f +
forwardInWallPlane * 0.2f +
wallAway * 0.4f +
wallUp * 0.4f;
jumpDirection = jumpDirection.normalized;
@@ -30,7 +30,7 @@ namespace Code.Gameplay.Character.Controller
private SlideMechanic _slideMechanic;
// private WallClimbMechanic _wallClimbMechanic;
private WallRunningMechanic _wallRunningMechanic;
//private WallLeanMechanic _wallLeanMechanic;
private WallLeanMechanic _wallLeanMechanic;
private GroundMovementSystem _groundMovement;
private AirMovementSystem _airMovement;
@@ -72,11 +72,8 @@ namespace Code.Gameplay.Character.Controller
_wallRunningMechanic.WallDetachImpulse = 7.0f;
_wallRunningMechanic.WallContactAngleThreshold = 45.0f;
/*_wallLeanMechanic = new WallLeanMechanic(
_characterConfig.maxWallLeanAngle,
_characterConfig.wallLeanSharpness,
_characterConfig.wallLeanHeightScale
);*/
_wallLeanMechanic = new WallLeanMechanic(
);
_groundMovement = new GroundMovementSystem(
_characterConfig.maxStableMoveSpeed,
_characterConfig.sprintMoveSpeed,
@@ -89,7 +86,7 @@ namespace Code.Gameplay.Character.Controller
motor.CharacterController = this; // Регистрация этого контроллера в движке
_inputHandler.FirstPersonCameraRotation =
motor.Transform.rotation; // Начальное вращение совпадает с персонажемс
//_wallLeanMechanic.SetCharacterTransform(characterTransform);
_wallLeanMechanic.SetModel(characterTransform);
_wallRunningMechanic.Initialize(motor, _inputHandler);
}
@@ -233,16 +230,15 @@ namespace Code.Gameplay.Character.Controller
{
_slideMechanic.EndSlide();
}
// if (motor.GroundingStatus.IsStableOnGround)
// {
// _wallClimbMechanic.ResetWallClimbSession();
// }
}
public void AfterCharacterUpdate(float deltaTime)
{
//_wallLeanMechanic.UpdateLean(motor, deltaTime);
_wallLeanMechanic.Update(
_wallRunningMechanic.IsWallRunning,
_wallRunningMechanic.GetWallNormal(),
motor.CharacterForward
);
}
public bool IsColliderValidForCollisions(Collider coll)
@@ -258,14 +254,9 @@ namespace Code.Gameplay.Character.Controller
public void OnMovementHit(Collider hitCollider, Vector3 hitNormal, Vector3 hitPoint,
ref HitStabilityReport hitStabilityReport)
{
// Попытка начать 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,