я ксиька :3

This commit is contained in:
MrTorex committed 2025-12-04 17:38:45 +03:00
1 parent b4de41894f
commit 4c16a63abd
1 file changed
+68 -107
@@ -7,14 +7,13 @@ namespace Code.Gameplay.Character.Controller.Abilities
{ {
public class WallRunningMechanic public class WallRunningMechanic
{ {
public float MaxWallRunTime = 1.75f; private readonly float _maxWallRunTime;
public float WallRunSpeed = 8.0f; private readonly float _wallRunSpeed;
public float WallRunAcceleration = 25.0f; private readonly float _wallRunAcceleration;
public float WallRunGravityScale = 0.25f; private readonly float _wallRunGravityScale;
public float WallDetachImpulse = 7.0f; private readonly float _wallDetachImpulse;
public float WallContactAngleThreshold = 45.0f; private readonly float _wallContactAngleThreshold;
private bool _isWallRunning;
private float _wallRunTimer; private float _wallRunTimer;
private Vector3 _wallNormal; private Vector3 _wallNormal;
private Vector3 _wallTangentRight; private Vector3 _wallTangentRight;
@@ -24,14 +23,11 @@ namespace Code.Gameplay.Character.Controller.Abilities
private float _surfaceAngleCos; private float _surfaceAngleCos;
private bool _canWallRun; private bool _canWallRun;
public float WallContactHeight { get; private set; }
public Vector3 WallRunStartForward { get; private set; }
private KinematicCharacterMotor _motor; private KinematicCharacterMotor _motor;
private CharacterInputHandler _inputHandler; private CharacterInputHandler _inputHandler;
public bool IsWallRunning => _isWallRunning; public bool IsWallRunning { get; private set; }
public WallRunningMechanic( public WallRunningMechanic(
float maxWallRunTime, float maxWallRunTime,
@@ -41,52 +37,44 @@ namespace Code.Gameplay.Character.Controller.Abilities
float wallDetachImpulse, float wallDetachImpulse,
float wallContactAngleThreshold) float wallContactAngleThreshold)
{ {
MaxWallRunTime = maxWallRunTime; _maxWallRunTime = maxWallRunTime;
WallRunSpeed = wallRunSpeed; _wallRunSpeed = wallRunSpeed;
WallRunAcceleration = wallRunAcceleration; _wallRunAcceleration = wallRunAcceleration;
WallRunGravityScale = wallRunGravityScale; _wallRunGravityScale = wallRunGravityScale;
WallDetachImpulse = wallDetachImpulse; _wallDetachImpulse = wallDetachImpulse;
WallContactAngleThreshold = wallContactAngleThreshold; _wallContactAngleThreshold = wallContactAngleThreshold;
} }
public void Initialize(KinematicCharacterMotor motor, CharacterInputHandler inputHandler) public void Initialize(KinematicCharacterMotor motor, CharacterInputHandler inputHandler)
{ {
_motor = motor; _motor = motor;
_inputHandler = inputHandler; _inputHandler = inputHandler;
_surfaceAngleCos = Mathf.Cos(Mathf.Deg2Rad * WallContactAngleThreshold); _surfaceAngleCos = Mathf.Cos(Mathf.Deg2Rad * _wallContactAngleThreshold);
}
public void Reset()
{
_isWallRunning = false;
_wallRunTimer = 0f;
_wallNormal = Vector3.zero;
} }
public void Tick(float deltaTime) public void Tick(float deltaTime)
{ {
if (_isWallRunning) if (!IsWallRunning) return;
_wallRunTimer += deltaTime;
if (_wallRunTimer > _maxWallRunTime)
{ {
_wallRunTimer += deltaTime; DetachFromWall(false);
if (_wallRunTimer > MaxWallRunTime)
{
DetachFromWall(false);
}
} }
} }
public bool TryStartWallRun(Vector3 hitNormal, Vector3 hitPoint) public bool TryStartWallRun(Vector3 hitNormal, Vector3 hitPoint)
{ {
if (_isWallRunning) return false; if (IsWallRunning) return false;
float dotWithUp = Vector3.Dot(hitNormal, _motor.CharacterUp); var dotWithUp = Vector3.Dot(hitNormal, _motor.CharacterUp);
bool isWall = Mathf.Abs(dotWithUp) < _surfaceAngleCos; var isWall = Mathf.Abs(dotWithUp) < _surfaceAngleCos;
if (!isWall) return false; if (!isWall) return false;
if (_motor.GroundingStatus.IsStableOnGround) return false; if (_motor.GroundingStatus.IsStableOnGround) return false;
float velocityIntoWall = Vector3.Dot(_motor.Velocity, -hitNormal); var velocityIntoWall = Vector3.Dot(_motor.Velocity, -hitNormal);
if (velocityIntoWall < 1f) return false; if (velocityIntoWall < 1f) return false;
StartWallRun(hitNormal, hitPoint); StartWallRun(hitNormal, hitPoint);
@@ -95,13 +83,11 @@ namespace Code.Gameplay.Character.Controller.Abilities
private void StartWallRun(Vector3 wallNormal, Vector3 contactPoint) private void StartWallRun(Vector3 wallNormal, Vector3 contactPoint)
{ {
_isWallRunning = true; IsWallRunning = true;
_wallRunTimer = 0f; _wallRunTimer = 0f;
_wallNormal = wallNormal.normalized; _wallNormal = wallNormal.normalized;
_wallContactPoint = contactPoint; _wallContactPoint = contactPoint;
WallContactHeight = contactPoint.y;
//_wallTangentUp = Vector3.ProjectOnPlane(_motor.CharacterUp, _wallNormal).normalized;
if (_wallTangentUp.sqrMagnitude < 0.01f) _wallTangentUp = Vector3.up; if (_wallTangentUp.sqrMagnitude < 0.01f) _wallTangentUp = Vector3.up;
_wallTangentRight = Vector3.Cross(_wallNormal, _wallTangentUp).normalized; _wallTangentRight = Vector3.Cross(_wallNormal, _wallTangentUp).normalized;
@@ -110,42 +96,41 @@ namespace Code.Gameplay.Character.Controller.Abilities
if (Vector3.Dot(_wallTangentUp, Vector3.up) < 0) if (Vector3.Dot(_wallTangentUp, Vector3.up) < 0)
_wallTangentUp = -_wallTangentUp; _wallTangentUp = -_wallTangentUp;
WallRunStartForward = _motor.CharacterForward;
_motor.ForceUnground(); _motor.ForceUnground();
} }
public void HandleWallRun(ref Vector3 currentVelocity, CharacterInputs inputs, float deltaTime) public void HandleWallRun(ref Vector3 currentVelocity, CharacterInputs inputs, float deltaTime)
{ {
if (!_isWallRunning) return; if (!IsWallRunning) return;
if (!IsWallStillPresent()) { DetachFromWall(false); return; } if (!IsWallStillPresent()) { DetachFromWall(false); return; }
Vector3 moveInputWorld = _inputHandler.MoveInputVector; var moveInputWorld = _inputHandler.MoveInputVector;
Vector3 moveInWallPlane = Vector3.ProjectOnPlane(moveInputWorld, _wallNormal); var moveInWallPlane = Vector3.ProjectOnPlane(moveInputWorld, _wallNormal);
Vector3 wsMove = Vector3.Project(moveInWallPlane, _wallTangentRight); var wsMove = Vector3.Project(moveInWallPlane, _wallTangentRight);
float ad = inputs.MoveAxisRight; var ad = inputs.MoveAxisRight;
float sideDot = Vector3.Dot(_wallNormal, _motor.CharacterRight); var sideDot = Vector3.Dot(_wallNormal, _motor.CharacterRight);
if (sideDot < 0) if (sideDot < 0)
ad = -ad; ad = -ad;
Vector3 adMove = _wallTangentUp * (-ad); var adMove = _wallTangentUp * (-ad);
Vector3 finalMove = wsMove + adMove; var finalMove = wsMove + adMove;
finalMove = finalMove.normalized * moveInputWorld.magnitude; finalMove = finalMove.normalized * moveInputWorld.magnitude;
Vector3 targetVelocity = finalMove * WallRunSpeed; var targetVelocity = finalMove * _wallRunSpeed;
currentVelocity = Vector3.Lerp( currentVelocity = Vector3.Lerp(
currentVelocity, currentVelocity,
targetVelocity, targetVelocity,
1f - Mathf.Exp(-WallRunAcceleration * deltaTime) 1f - Mathf.Exp(-_wallRunAcceleration * deltaTime)
); );
currentVelocity += Physics.gravity * (WallRunGravityScale * deltaTime); currentVelocity += Physics.gravity * (_wallRunGravityScale * deltaTime);
float distanceToWall = Vector3.Dot(_motor.TransientPosition - _wallContactPoint, _wallNormal); var distanceToWall = Vector3.Dot(_motor.TransientPosition - _wallContactPoint, _wallNormal);
if (Mathf.Abs(distanceToWall) > 0.8f) if (Mathf.Abs(distanceToWall) > 0.8f)
{ {
DetachFromWall(false); DetachFromWall(false);
@@ -160,16 +145,15 @@ namespace Code.Gameplay.Character.Controller.Abilities
private bool IsWallStillPresent() private bool IsWallStillPresent()
{ {
if (_motor == null) return false; if (!_motor) return false;
Vector3 characterCenter = _motor.TransientPosition; var characterCenter = _motor.TransientPosition;
Vector3 characterForward = _motor.CharacterForward; var characterRight = _motor.CharacterRight;
Vector3 characterRight = _motor.CharacterRight; var characterUp = _motor.CharacterUp;
Vector3 characterUp = _motor.CharacterUp;
float checkDistance = 0.45f; const float checkDistance = 0.45f;
Vector3 rayOrigin = characterCenter + characterUp * 0.2f; var rayOrigin = characterCenter + characterUp * 0.2f;
Vector3 rayDir = -_wallNormal; var rayDir = -_wallNormal;
Vector3[] offsets = { Vector3[] offsets = {
Vector3.zero, Vector3.zero,
@@ -181,83 +165,60 @@ namespace Code.Gameplay.Character.Controller.Abilities
foreach (var offset in offsets) foreach (var offset in offsets)
{ {
if (Physics.Raycast(rayOrigin + offset, rayDir, out RaycastHit hit, checkDistance, if (!Physics.Raycast(rayOrigin + offset, rayDir, out var hit, checkDistance,
_motor.CollidableLayers, QueryTriggerInteraction.Ignore)) _motor.CollidableLayers, QueryTriggerInteraction.Ignore)) continue;
{
float dotWithUp = Mathf.Abs(Vector3.Dot(hit.normal, characterUp));
if (dotWithUp < _surfaceAngleCos)
{
_wallNormal = hit.normal.normalized;
_wallContactPoint = hit.point;
/*_wallTangentUp = Vector3.ProjectOnPlane(_motor.CharacterUp, _wallNormal).normalized; var dotWithUp = Mathf.Abs(Vector3.Dot(hit.normal, characterUp));
if (_wallTangentUp.sqrMagnitude < 0.01f) _wallTangentUp = Vector3.up;
_wallTangentRight = Vector3.Cross(_wallNormal, _wallTangentUp).normalized;
*/
return true; if (!(dotWithUp < _surfaceAngleCos)) continue;
}
} _wallNormal = hit.normal.normalized;
_wallContactPoint = hit.point;
return true;
} }
return false; return false;
} }
public void DetachFromWall(bool withImpulse) private void DetachFromWall(bool withImpulse)
{ {
if (!_isWallRunning) return; if (!IsWallRunning) return;
_isWallRunning = false; IsWallRunning = false;
if (withImpulse && _motor != null) if (!withImpulse || !_motor) return;
{
Vector3 detachImpulse = -_wallNormal * WallDetachImpulse; var detachImpulse = -_wallNormal * _wallDetachImpulse;
_motor.BaseVelocity += detachImpulse; _motor.BaseVelocity += detachImpulse;
}
} }
public Vector3 GetWallNormal() => _wallNormal; 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;
}
public void PerformWallJump(CharacterInputs inputs, Quaternion cameraRotation) public void PerformWallJump(CharacterInputs inputs, Quaternion cameraRotation)
{ {
if (!_isWallRunning || _motor == null) return; if (!IsWallRunning || !_motor) return;
_isWallRunning = false; IsWallRunning = false;
Vector3 wallAway = _wallNormal; var wallAway = _wallNormal;
Vector3 wallUp = _wallTangentUp; var wallUp = _wallTangentUp;
Vector3 playerForward = cameraRotation * Vector3.forward; var playerForward = cameraRotation * Vector3.forward;
Vector3 forwardInWallPlane = Vector3.ProjectOnPlane(playerForward, _wallNormal).normalized; var forwardInWallPlane = Vector3.ProjectOnPlane(playerForward, _wallNormal).normalized;
if (forwardInWallPlane.sqrMagnitude < 0.01f) forwardInWallPlane = _wallTangentRight; if (forwardInWallPlane.sqrMagnitude < 0.01f) forwardInWallPlane = _wallTangentRight;
Vector3 jumpDirection = var jumpDirection =
forwardInWallPlane * 0.2f + forwardInWallPlane * 0.2f +
wallAway * 0.4f + wallAway * 0.4f +
wallUp * 0.4f; wallUp * 0.4f;
jumpDirection = jumpDirection.normalized; jumpDirection = jumpDirection.normalized;
float wallJumpForce = WallDetachImpulse * 2f; var wallJumpForce = _wallDetachImpulse * 2f;
Vector3 jumpImpulse = jumpDirection * wallJumpForce; var jumpImpulse = jumpDirection * wallJumpForce;
_motor.BaseVelocity += jumpImpulse; _motor.BaseVelocity += jumpImpulse;