Your “rest angle” goes lower because you are applying the mouse input to the base pitch while the recoil pitch is still active. When the recoil returns to zero, the base pitch ends up lower than before. To allow “counteracting” the recoil without shifting the resting angle, separate the base view and the recoil offset, and consume the opposing input in the recoil first (bleed), so the resting angle remains unchanged.
// Campos
float _viewPitch; // ángulo base (input)
float _viewYaw; // ángulo base (input)
Vector3 _recoilTarget; // objetivo de recoil (se empuja en ApplyRecoil)
Vector3 _recoilCurrent; // recoil actual (interpola hacia target)
[SerializeField] float _rotationSpeed = 0.1f;
[SerializeField] float _recoilReturn = 10f; // velocidad retorno a 0 del target
[SerializeField] float _recoilFollow = 5f; // seguimiento current->target
[SerializeField] float _bottomClamp = -80f, _topClamp = 80f;
void Update()
{
Vector2 look = _lookAction.ReadValue<Vector2>();
float dt = Time.deltaTime;
float yawVel = look.x * _rotationSpeed;
float pitchVel = look.y * _rotationSpeed;
// 1) Actualiza targets de recoil hacia 0 (retorno natural)
_recoilTarget = Vector3.Lerp(_recoilTarget, Vector3.zero, _recoilReturn * dt);
// 2) Sigue el target (suavizado del recoil visible)
_recoilCurrent = Vector3.Lerp(_recoilCurrent, _recoilTarget, _recoilFollow * dt);
// 3) BLEED: si hay recoil positivo en pitch (mirada se fue arriba)
// y el jugador tira hacia abajo (pitchVel < 0),
// consume primero esa entrada en el recoil, NO en la vista base.
if (_recoilCurrent.x > 0f && pitchVel < 0f)
{
// cuánto “abajo” intenta mover este frame en grados
float intendedDown = -pitchVel * dt;
// sangramos contra el recoil sin pasar de 0
float bleed = Mathf.Min(_recoilCurrent.x, intendedDown);
_recoilCurrent.x -= bleed;
// neutraliza esa parte del input para que no afecte al ángulo base
pitchVel += (bleed / dt);
}
// (Opcional) Lo mismo para yaw si usas recoil lateral:
if (Mathf.Abs(_recoilCurrent.y) > 0f && Mathf.Abs(yawVel) > 0f)
{
// ejemplo simple: si el input es opuesto al signo del recoil, bleedea algo
float intendedYaw = yawVel * dt;
if (Mathf.Sign(intendedYaw) == -Mathf.Sign(_recoilCurrent.y))
{
float bleedYaw = Mathf.Min(Mathf.Abs(_recoilCurrent.y), Mathf.Abs(intendedYaw));
_recoilCurrent.y -= Mathf.Sign(_recoilCurrent.y) * bleedYaw;
yawVel -= Mathf.Sign(intendedYaw) * (bleedYaw / dt);
}
}
// 4) Aplica el input restante SOLO a la vista base
_viewPitch = Mathf.Clamp(_viewPitch + pitchVel, _bottomClamp, _topClamp);
_viewYaw += yawVel;
// 5) Aplica suma base + recoil al transform/cabeza
_Head.localRotation = Quaternion.Euler(_viewPitch + _recoilCurrent.x, 0f, 0f);
transform.rotation = Quaternion.Euler(0f, _viewYaw + _recoilCurrent.y, 0f);
}
public void ApplyRecoil(Vector2 recoil) // recoil.x = yaw, recoil.y = pitch
{
// Empuja el target; el current lo seguirá con suavizado
_recoilTarget.x += recoil.y; // pitch up
_recoilTarget.y += Random.Range(-recoil.x, recoil.x); // yaw shake
}