Merge branch 'Dev' into Levels2

This commit is contained in:
ThomasCharester committed 2025-12-04 22:02:16 +03:00
commit 7e1f7cecc2
4 files changed
+167 -32

No files matched your search

@@ -23,7 +23,11 @@ namespace Code.Configs
); );
public float public float
slideMinVelocityThreshold = 3f, slideMinVelocityThreshold = 3f,
slideCooldown = 1.5f; slideCooldown = 1.5f,
slideColliderHeightScale = 0.5f,
slideCameraOffsetY = -0.5f,
slideEnterDuration = 0.2f,
slideExitDuration = 0.25f;
public float public float
leanAngle = 25f, leanAngle = 25f,
@@ -1,4 +1,3 @@
using Code.Gameplay.Character.Controller.Input;
using Code.Gameplay.Character.Core; using Code.Gameplay.Character.Core;
using KinematicCharacterController; using KinematicCharacterController;
using UnityEngine; using UnityEngine;
@@ -25,7 +24,6 @@ namespace Code.Gameplay.Character.Controller.Abilities
private bool _canWallRun; private bool _canWallRun;
private KinematicCharacterMotor _motor; private KinematicCharacterMotor _motor;
private CharacterInputHandler _inputHandler;
public bool IsWallRunning { get; private set; } public bool IsWallRunning { get; private set; }
@@ -45,10 +43,9 @@ namespace Code.Gameplay.Character.Controller.Abilities
_wallContactAngleThreshold = wallContactAngleThreshold; _wallContactAngleThreshold = wallContactAngleThreshold;
} }
public void Initialize(KinematicCharacterMotor motor, CharacterInputHandler inputHandler) public void Initialize(KinematicCharacterMotor motor)
{ {
_motor = motor; _motor = motor;
_inputHandler = inputHandler;
_surfaceAngleCos = Mathf.Cos(Mathf.Deg2Rad * _wallContactAngleThreshold); _surfaceAngleCos = Mathf.Cos(Mathf.Deg2Rad * _wallContactAngleThreshold);
} }
@@ -88,13 +85,16 @@ namespace Code.Gameplay.Character.Controller.Abilities
_wallNormal = wallNormal.normalized; _wallNormal = wallNormal.normalized;
_wallContactPoint = contactPoint; _wallContactPoint = contactPoint;
if (_wallTangentUp.sqrMagnitude < 0.01f) _wallTangentUp = Vector3.up; var forwardInWallPlane = Vector3.ProjectOnPlane(_motor.CharacterForward, _wallNormal).normalized;
if (forwardInWallPlane.sqrMagnitude < 0.01f)
{
var fallbackRight = Vector3.Cross(_wallNormal, _wallTangentUp).normalized;
var dotWithRight = Vector3.Dot(_motor.CharacterRight, _wallNormal);
forwardInWallPlane = dotWithRight < 0 ? fallbackRight : -fallbackRight;
}
_wallTangentRight = Vector3.Cross(_wallNormal, _wallTangentUp).normalized; _wallTangentRight = forwardInWallPlane;
_wallTangentUp = Vector3.Cross(_wallTangentRight, -_wallNormal).normalized;
_wallTangentUp = Vector3.Cross(_wallTangentRight, _wallNormal).normalized;
if (Vector3.Dot(_wallTangentUp, Vector3.up) < 0)
_wallTangentUp = -_wallTangentUp;
_motor.ForceUnground(); _motor.ForceUnground();
} }
@@ -104,23 +104,11 @@ namespace Code.Gameplay.Character.Controller.Abilities
if (!IsWallRunning) return; if (!IsWallRunning) return;
if (!IsWallStillPresent()) { DetachFromWall(false); return; } if (!IsWallStillPresent()) { DetachFromWall(false); return; }
var moveInputWorld = _inputHandler.MoveInputVector; var forwardInput = inputs.MoveAxisForward;
var moveInWallPlane = Vector3.ProjectOnPlane(moveInputWorld, _wallNormal); var moveDirection = _wallTangentRight * forwardInput;
var wsMove = Vector3.Project(moveInWallPlane, _wallTangentRight);
var ad = inputs.MoveAxisRight; var targetSpeed = moveDirection.sqrMagnitude > 0.01f ? _wallRunSpeed : currentVelocity.magnitude;
var targetVelocity = moveDirection.normalized * targetSpeed;
var sideDot = Vector3.Dot(_wallNormal, _motor.CharacterRight);
if (sideDot < 0)
ad = -ad;
var adMove = _wallTangentUp * (-ad);
var finalMove = wsMove + adMove;
finalMove = finalMove.normalized * moveInputWorld.magnitude;
var targetVelocity = finalMove * _wallRunSpeed;
currentVelocity = Vector3.Lerp( currentVelocity = Vector3.Lerp(
currentVelocity, currentVelocity,
@@ -146,6 +134,8 @@ namespace Code.Gameplay.Character.Controller.Abilities
private bool IsWallStillPresent() private bool IsWallStillPresent()
{ {
if (!_motor) return false; if (!_motor) return false;
if (_motor.GroundingStatus.IsStableOnGround) return false;
var characterCenter = _motor.TransientPosition; var characterCenter = _motor.TransientPosition;
var characterRight = _motor.CharacterRight; var characterRight = _motor.CharacterRight;
@@ -15,7 +15,7 @@ namespace Code.Gameplay.Character.Controller
* Реализует физическое движение персонажа с использованием Kinematic 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 public class CustomCharacterController : MonoBehaviour, ICharacterController
{ {
@@ -25,6 +25,20 @@ namespace Code.Gameplay.Character.Controller
[SerializeField] private Transform characterTransform; // Трансформ персонажа (с точкой отслеживания камеры) [SerializeField] private Transform characterTransform; // Трансформ персонажа (с точкой отслеживания камеры)
[SerializeField] private CapsuleCollider _capsuleCollider;
[SerializeField] private Transform _cameraFollowPoint;
private Vector3 _originalColliderCenter;
private float _originalColliderHeight;
private Vector3 _originalFollowPointLocalPosition;
private bool _isEnteringSlide;
private bool _isExitingSlide;
private float _slideTransitionTime;
private float _slideTransitionDuration;
private float _currentColliderHeight;
private Vector3 _currentColliderCenter;
private Vector3 _currentFollowPointLocalPosition;
private bool _wasSlidingLastFrame;
// Подсистемы // Подсистемы
private CharacterInputHandler _inputHandler; private CharacterInputHandler _inputHandler;
private DashMechanic _dashMechanic; private DashMechanic _dashMechanic;
@@ -91,7 +105,15 @@ namespace Code.Gameplay.Character.Controller
_inputHandler.FirstPersonCameraRotation = _inputHandler.FirstPersonCameraRotation =
motor.Transform.rotation; // Начальное вращение совпадает с персонажемс motor.Transform.rotation; // Начальное вращение совпадает с персонажемс
_wallLeanMechanic.SetModel(characterTransform); _wallLeanMechanic.SetModel(characterTransform);
_wallRunningMechanic.Initialize(motor, _inputHandler); _wallRunningMechanic.Initialize(motor);
_originalColliderCenter = _capsuleCollider.center;
_originalColliderHeight = _capsuleCollider.height;
_originalFollowPointLocalPosition = _cameraFollowPoint.localPosition;
_currentColliderHeight = _originalColliderHeight;
_currentColliderCenter = _originalColliderCenter;
_currentFollowPointLocalPosition = _originalFollowPointLocalPosition;
} }
// Текущий режим камеры для адаптации поведения // Текущий режим камеры для адаптации поведения
@@ -184,14 +206,32 @@ namespace Code.Gameplay.Character.Controller
return; return;
} }
if (!_slideMechanic.IsSliding) var wasSliding = _wasSlidingLastFrame;
var isSlidingNow = _slideMechanic.IsSliding;
if (!isSlidingNow)
{
_slideMechanic.TryStartSlide(motor, _currentCameraMode, _inputHandler.FirstPersonCameraRotation, _inputHandler.LookInputVector); _slideMechanic.TryStartSlide(motor, _currentCameraMode, _inputHandler.FirstPersonCameraRotation, _inputHandler.LookInputVector);
else isSlidingNow = _slideMechanic.IsSliding;
}
switch (isSlidingNow)
{
case true when !wasSliding:
EnterSlideState();
break;
case false when wasSliding:
ExitSlideState();
break;
}
if (isSlidingNow)
{ {
_slideMechanic.HandleSlide(ref currentVelocity, deltaTime, motor); _slideMechanic.HandleSlide(ref currentVelocity, deltaTime, motor);
return;
} }
_wasSlidingLastFrame = isSlidingNow;
// === WALL RUNNING: обработка состояния === // === WALL RUNNING: обработка состояния ===
if (_wallRunningMechanic.IsWallRunning) if (_wallRunningMechanic.IsWallRunning)
{ {
@@ -226,6 +266,103 @@ namespace Code.Gameplay.Character.Controller
isAbilitiesBlocked = true; isAbilitiesBlocked = true;
abilityBlockTimer = duration; abilityBlockTimer = duration;
} }
private void EnterSlideState()
{
_isEnteringSlide = true;
_isExitingSlide = false;
_slideTransitionTime = 0f;
_slideTransitionDuration = _characterConfig.slideEnterDuration;
}
private void ExitSlideState()
{
_isExitingSlide = true;
_isEnteringSlide = false;
_slideTransitionTime = 0f;
_slideTransitionDuration = _characterConfig.slideExitDuration;
}
private void UpdateSlideTransition(float deltaTime)
{
if (!_isEnteringSlide && !_isExitingSlide) return;
_slideTransitionTime += deltaTime;
var t = Mathf.Clamp01(_slideTransitionTime / _slideTransitionDuration);
t = Mathf.SmoothStep(0f, 1f, t);
if (_isEnteringSlide)
{
var targetHeight = _originalColliderHeight * _characterConfig.slideColliderHeightScale;
var targetCenterY = targetHeight * 0.5f - _originalColliderHeight * 0.5f;
var targetCenter = _originalColliderCenter + Vector3.up * targetCenterY;
var targetFollowPos = _originalFollowPointLocalPosition + Vector3.up * _characterConfig.slideCameraOffsetY;
_currentColliderHeight = Mathf.Lerp(_originalColliderHeight, targetHeight, t);
_currentColliderCenter = Vector3.Lerp(_originalColliderCenter, targetCenter, t);
_currentFollowPointLocalPosition = Vector3.Lerp(_originalFollowPointLocalPosition, targetFollowPos, t);
}
else if (_isExitingSlide)
{
_currentColliderHeight = Mathf.Lerp(_originalColliderHeight * _characterConfig.slideColliderHeightScale, _originalColliderHeight, t);
var prevHeight = _originalColliderHeight * _characterConfig.slideColliderHeightScale;
var prevCenterY = prevHeight * 0.5f - _originalColliderHeight * 0.5f;
var prevCenter = _originalColliderCenter + Vector3.up * prevCenterY;
var prevFollowPos = _originalFollowPointLocalPosition + Vector3.up * _characterConfig.slideCameraOffsetY;
_currentColliderCenter = Vector3.Lerp(prevCenter, _originalColliderCenter, t);
_currentFollowPointLocalPosition = Vector3.Lerp(prevFollowPos, _originalFollowPointLocalPosition, t);
}
if (_capsuleCollider)
{
_capsuleCollider.height = _currentColliderHeight;
_capsuleCollider.center = _currentColliderCenter;
}
if (_cameraFollowPoint)
{
_cameraFollowPoint.localPosition = _currentFollowPointLocalPosition;
}
if (!(t >= 1f)) return;
if (_isEnteringSlide)
{
_isEnteringSlide = false;
if (_capsuleCollider)
{
_capsuleCollider.height = _originalColliderHeight * _characterConfig.slideColliderHeightScale;
var h = _capsuleCollider.height;
_capsuleCollider.center = _originalColliderCenter + Vector3.up * (h * 0.5f - _originalColliderHeight * 0.5f);
}
if (_cameraFollowPoint)
{
_cameraFollowPoint.localPosition = _originalFollowPointLocalPosition + Vector3.up * _characterConfig.slideCameraOffsetY;
}
}
else if (_isExitingSlide)
{
_isExitingSlide = false;
if (_capsuleCollider)
{
_capsuleCollider.height = _originalColliderHeight;
_capsuleCollider.center = _originalColliderCenter;
}
if (_cameraFollowPoint)
{
_cameraFollowPoint.localPosition = _originalFollowPointLocalPosition;
}
}
_currentColliderHeight = _capsuleCollider ? _capsuleCollider.height : _originalColliderHeight;
_currentColliderCenter = _capsuleCollider ? _capsuleCollider.center : _originalColliderCenter;
_currentFollowPointLocalPosition = _cameraFollowPoint ? _cameraFollowPoint.localPosition : _originalFollowPointLocalPosition;
}
public void BeforeCharacterUpdate(float deltaTime) public void BeforeCharacterUpdate(float deltaTime)
{ {
@@ -241,6 +378,8 @@ namespace Code.Gameplay.Character.Controller
public void AfterCharacterUpdate(float deltaTime) public void AfterCharacterUpdate(float deltaTime)
{ {
UpdateSlideTransition(deltaTime);
_wallLeanMechanic.Update( _wallLeanMechanic.Update(
_wallRunningMechanic.IsWallRunning, _wallRunningMechanic.IsWallRunning,
_wallRunningMechanic.GetWallNormal(), _wallRunningMechanic.GetWallNormal(),
@@ -173,6 +173,8 @@ MonoBehaviour:
m_EditorClassIdentifier: Assembly-CSharp::Code.Gameplay.Player.CharacterController m_EditorClassIdentifier: Assembly-CSharp::Code.Gameplay.Player.CharacterController
motor: {fileID: 8476708390679839925} motor: {fileID: 8476708390679839925}
characterTransform: {fileID: 4122797476271397740} characterTransform: {fileID: 4122797476271397740}
_capsuleCollider: {fileID: 8541228187116599584}
_cameraFollowPoint: {fileID: 1047840484662430186}
--- !u!114 &3283650228831796584 --- !u!114 &3283650228831796584
MonoBehaviour: MonoBehaviour:
m_ObjectHideFlags: 0 m_ObjectHideFlags: 0