Files
Ultimate-Fishing-Simulator-…/Assets/Scripts/Assembly-CSharp/RootMotion/FinalIK/Recoil.cs
2026-03-04 09:37:33 +08:00

299 lines
8.1 KiB
C#

using System;
using UnityEngine;
namespace RootMotion.FinalIK
{
public class Recoil : OffsetModifier
{
[Serializable]
public class RecoilOffset
{
[Serializable]
public class EffectorLink
{
[Tooltip("Type of the FBBIK effector to use")]
public FullBodyBipedEffector effector;
[Tooltip("Weight of using this effector")]
public float weight;
}
[Tooltip("Offset vector for the associated effector when doing recoil.")]
public Vector3 offset;
[Tooltip("When firing before the last recoil has faded, how much of the current recoil offset will be maintained?")]
[Range(0f, 1f)]
public float additivity = 1f;
[Tooltip("Max additive recoil for automatic fire.")]
public float maxAdditiveOffsetMag = 0.2f;
[Tooltip("Linking this recoil offset to FBBIK effectors.")]
public EffectorLink[] effectorLinks;
private Vector3 additiveOffset;
private Vector3 lastOffset;
public void Start()
{
if (!(additivity <= 0f))
{
additiveOffset = Vector3.ClampMagnitude(lastOffset * additivity, maxAdditiveOffsetMag);
}
}
public void Apply(IKSolverFullBodyBiped solver, Quaternion rotation, float masterWeight, float length, float timeLeft)
{
additiveOffset = Vector3.Lerp(Vector3.zero, additiveOffset, timeLeft / length);
lastOffset = rotation * (offset * masterWeight) + rotation * additiveOffset;
EffectorLink[] array = effectorLinks;
foreach (EffectorLink effectorLink in array)
{
solver.GetEffector(effectorLink.effector).positionOffset += lastOffset * effectorLink.weight;
}
}
}
[Serializable]
public enum Handedness
{
Right = 0,
Left = 1
}
[Tooltip("Reference to the AimIK component. Optional, only used to getting the aiming direction.")]
public AimIK aimIK;
[Tooltip("Optional head AimIK solver. This solver should only use neck and head bones and have the head as Aim Transform")]
public AimIK headIK;
[Tooltip("Set this true if you are using IKExecutionOrder.cs or a custom script to force AimIK solve after FBBIK.")]
public bool aimIKSolvedLast;
[Tooltip("Which hand is holding the weapon?")]
public Handedness handedness;
[Tooltip("Check for 2-handed weapons.")]
public bool twoHanded = true;
[Tooltip("Weight curve for the recoil offsets. Recoil procedure is as long as this curve.")]
public AnimationCurve recoilWeight;
[Tooltip("How much is the magnitude randomized each time Recoil is called?")]
public float magnitudeRandom = 0.1f;
[Tooltip("How much is the rotation randomized each time Recoil is called?")]
public Vector3 rotationRandom;
[Tooltip("Rotating the primary hand bone for the recoil (in local space).")]
public Vector3 handRotationOffset;
[Tooltip("Time of blending in another recoil when doing automatic fire.")]
public float blendTime;
[Space(10f)]
[Tooltip("FBBIK effector position offsets for the recoil (in aiming direction space).")]
public RecoilOffset[] offsets;
[HideInInspector]
public Quaternion rotationOffset = Quaternion.identity;
private float magnitudeMlp = 1f;
private float endTime = -1f;
private Quaternion handRotation;
private Quaternion secondaryHandRelativeRotation;
private Quaternion randomRotation;
private float length = 1f;
private bool initiated;
private float blendWeight;
private float w;
private Quaternion primaryHandRotation = Quaternion.identity;
private bool handRotationsSet;
private Vector3 aimIKAxis;
public bool isFinished => Time.time > endTime;
private IKEffector primaryHandEffector
{
get
{
if (handedness == Handedness.Right)
{
return ik.solver.rightHandEffector;
}
return ik.solver.leftHandEffector;
}
}
private IKEffector secondaryHandEffector
{
get
{
if (handedness == Handedness.Right)
{
return ik.solver.leftHandEffector;
}
return ik.solver.rightHandEffector;
}
}
private Transform primaryHand => primaryHandEffector.bone;
private Transform secondaryHand => secondaryHandEffector.bone;
public void SetHandRotations(Quaternion leftHandRotation, Quaternion rightHandRotation)
{
if (handedness == Handedness.Left)
{
primaryHandRotation = leftHandRotation;
}
else
{
primaryHandRotation = rightHandRotation;
}
handRotationsSet = true;
}
public void Fire(float magnitude)
{
float num = magnitude * UnityEngine.Random.value * magnitudeRandom;
magnitudeMlp = magnitude + num;
randomRotation = Quaternion.Euler(rotationRandom * UnityEngine.Random.value);
RecoilOffset[] array = offsets;
for (int i = 0; i < array.Length; i++)
{
array[i].Start();
}
if (Time.time < endTime)
{
blendWeight = 0f;
}
else
{
blendWeight = 1f;
}
length = recoilWeight.keys[^1].time;
endTime = Time.time + length;
}
protected override void OnModifyOffset()
{
if (aimIK != null)
{
aimIKAxis = aimIK.solver.axis;
}
if (!initiated && ik != null)
{
initiated = true;
if (headIK != null)
{
headIK.enabled = false;
}
IKSolverFullBodyBiped solver = ik.solver;
solver.OnPostUpdate = (IKSolver.UpdateDelegate)Delegate.Combine(solver.OnPostUpdate, new IKSolver.UpdateDelegate(AfterFBBIK));
if (aimIK != null)
{
IKSolverAim solver2 = aimIK.solver;
solver2.OnPostUpdate = (IKSolver.UpdateDelegate)Delegate.Combine(solver2.OnPostUpdate, new IKSolver.UpdateDelegate(AfterAimIK));
}
}
if (Time.time >= endTime)
{
rotationOffset = Quaternion.identity;
return;
}
blendTime = Mathf.Max(blendTime, 0f);
if (blendTime > 0f)
{
blendWeight = Mathf.Min(blendWeight + Time.deltaTime * (1f / blendTime), 1f);
}
else
{
blendWeight = 1f;
}
float b = recoilWeight.Evaluate(length - (endTime - Time.time)) * magnitudeMlp;
w = Mathf.Lerp(w, b, blendWeight);
Quaternion quaternion = ((aimIK != null && aimIK.solver.transform != null && !aimIKSolvedLast) ? Quaternion.LookRotation(aimIK.solver.IKPosition - aimIK.solver.transform.position, ik.references.root.up) : ik.references.root.rotation);
quaternion = randomRotation * quaternion;
RecoilOffset[] array = offsets;
for (int i = 0; i < array.Length; i++)
{
array[i].Apply(ik.solver, quaternion, w, length, endTime - Time.time);
}
if (!handRotationsSet)
{
primaryHandRotation = primaryHand.rotation;
}
handRotationsSet = false;
rotationOffset = Quaternion.Lerp(Quaternion.identity, Quaternion.Euler(randomRotation * primaryHandRotation * handRotationOffset), w);
handRotation = rotationOffset * primaryHandRotation;
if (twoHanded)
{
Vector3 vector = Quaternion.Inverse(primaryHand.rotation) * (secondaryHand.position - primaryHand.position);
secondaryHandRelativeRotation = Quaternion.Inverse(primaryHand.rotation) * secondaryHand.rotation;
Vector3 vector2 = primaryHand.position + primaryHandEffector.positionOffset + handRotation * vector;
secondaryHandEffector.positionOffset += vector2 - (secondaryHand.position + secondaryHandEffector.positionOffset);
}
if (aimIK != null && aimIKSolvedLast)
{
aimIK.solver.axis = Quaternion.Inverse(ik.references.root.rotation) * Quaternion.Inverse(rotationOffset) * aimIKAxis;
}
}
private void AfterFBBIK()
{
if (Time.time < endTime)
{
primaryHand.rotation = handRotation;
if (twoHanded)
{
secondaryHand.rotation = primaryHand.rotation * secondaryHandRelativeRotation;
}
}
if (!aimIKSolvedLast && headIK != null)
{
headIK.solver.Update();
}
}
private void AfterAimIK()
{
if (aimIKSolvedLast)
{
aimIK.solver.axis = aimIKAxis;
}
if (aimIKSolvedLast && headIK != null)
{
headIK.solver.Update();
}
}
protected override void OnDestroy()
{
base.OnDestroy();
if (ik != null && initiated)
{
IKSolverFullBodyBiped solver = ik.solver;
solver.OnPostUpdate = (IKSolver.UpdateDelegate)Delegate.Remove(solver.OnPostUpdate, new IKSolver.UpdateDelegate(AfterFBBIK));
if (aimIK != null)
{
IKSolverAim solver2 = aimIK.solver;
solver2.OnPostUpdate = (IKSolver.UpdateDelegate)Delegate.Remove(solver2.OnPostUpdate, new IKSolver.UpdateDelegate(AfterAimIK));
}
}
}
}
}