Skip to content

Instantly share code, notes, and snippets.

@pppoe252110
Last active April 9, 2025 19:03
Show Gist options
  • Save pppoe252110/068db8a845f817570d9dde87c173d930 to your computer and use it in GitHub Desktop.
Save pppoe252110/068db8a845f817570d9dde87c173d930 to your computer and use it in GitHub Desktop.
Correct Weapon Sway implementation which does not depend on fps
using UnityEngine;
public class WeaponSway : MonoBehaviour
{
[SerializeField] private Transform weaponTransform;
[Header("Sway Properties")]
[SerializeField] private float swaySmooth = 8f;
[SerializeField] private float swayDamp = 2f;
[SerializeField] private float posToRotAmount = 1f;
[SerializeField] private float rotToPosAmount = .1f;
[SerializeField] private AnimationCurve swayOverDistance = AnimationCurve.EaseInOut(0, 0, 1, 1);
[Header("Rotation Sway")]
[SerializeField] private float rotationSwayMultiplier = -.2f;
[SerializeField] private float maxRotSwayAmount = 5f;
[Header("Position Sway")]
[SerializeField] private float movementSwayMultiplier = -0.05f;
[SerializeField] private float maxPosSwayAmount = 0.01f;
private Vector3 _initialPosition;
private Quaternion _initialRotation;
private Vector2 _rotSway;
private Vector3 _posSway;
private Quaternion _lastRotation;
private Vector3 _lastPosition;
private void Start()
{
if (!weaponTransform)
weaponTransform = transform.GetChild(0);
CacheInitialTransforms();
}
private void CacheInitialTransforms()
{
_lastRotation = transform.localRotation;
_lastPosition = transform.position;
_initialPosition = weaponTransform.localPosition;
_initialRotation = weaponTransform.localRotation;
}
private void LateUpdate()
{
CalculateSway();
ApplySway();
}
private void CalculateSway()
{
CalculateRotationalSway();
CalculatePositionalSway();
ClampSwayValues();
}
private void CalculateRotationalSway()
{
Quaternion angularVelocity = Quaternion.Inverse(_lastRotation) * transform.rotation;
_lastRotation = transform.rotation;
float mouseX = FixAngle(angularVelocity.eulerAngles.y) * rotationSwayMultiplier;
float mouseY = -FixAngle(angularVelocity.eulerAngles.x) * rotationSwayMultiplier;
Vector2 result = new Vector2(mouseX, mouseY);
result *= swayOverDistance.Evaluate(1f - _rotSway.magnitude / maxRotSwayAmount);
_rotSway += result;
}
private void CalculatePositionalSway()
{
Vector3 positionDelta = transform.position - _lastPosition;
_lastPosition = transform.position;
positionDelta = weaponTransform.InverseTransformDirection(positionDelta) * movementSwayMultiplier;
positionDelta *= swayOverDistance.Evaluate(1f - _posSway.magnitude / maxPosSwayAmount);
_posSway += positionDelta;
}
private void ClampSwayValues()
{
_rotSway = Vector2.ClampMagnitude(_rotSway, maxRotSwayAmount);
_posSway = Vector3.ClampMagnitude(_posSway, maxPosSwayAmount);
}
private void ApplySway()
{
float sway = swaySmooth * Time.deltaTime;
ApplyRotationSway(sway);
ApplyPositionSway(sway);
ReduceSwayOverTime(sway * swayDamp, sway * swayDamp);
}
private void ApplyRotationSway(float deltaRot)
{
Quaternion targetRotation = _initialRotation * Quaternion.Euler(new Vector3(-_rotSway.y - _posSway.y * posToRotAmount * Mathf.Rad2Deg, _rotSway.x + _posSway.x * posToRotAmount * Mathf.Rad2Deg, 0));
weaponTransform.localRotation = Quaternion.Lerp(weaponTransform.localRotation, targetRotation, deltaRot * swaySmooth);
}
private void ApplyPositionSway(float deltaPos)
{
Vector3 targetPosition = _initialPosition + _posSway + new Vector3(rotToPosAmount * _rotSway.x * Mathf.Deg2Rad, rotToPosAmount * _rotSway.y * Mathf.Deg2Rad);
weaponTransform.localPosition = Vector3.Lerp(weaponTransform.localPosition, targetPosition, deltaPos * swaySmooth);
}
private void ReduceSwayOverTime(float deltaRot, float deltaPos)
{
_rotSway = Vector2.Lerp(_rotSway, Vector2.zero, deltaRot);
_posSway = Vector3.Lerp(_posSway, Vector3.zero, deltaPos);
}
private float FixAngle(float angle)
{
// Normalize the angle to be within -180 to 180 degrees
return Mathf.Repeat(angle + 180f, 360f) - 180f;
}
}
@pppoe252110
Copy link
Author

Please Check My Other Gists :> They Will Help!

@Juanmita2k
Copy link

Thank u man, this is such an amazing job

@denoboi
Copy link

denoboi commented May 7, 2024

Hey, why did you add 'serializefield' to the 'public' fields?

@NicklasSchell
Copy link

Amazing, thank you so much

@pppoe252110
Copy link
Author

Hey, why did you add 'serializefield' to the 'public' fields?

cuz I'm stupid haha

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment