Search code examples
c#unity-game-engineanimationgame-developmentanimator

Unity Animator component unnecessarily overriding scripted position of Game Objects


I have a hand and pistol(fps) gameObject. and the recoil system is controlled directly from script (including the slide on the pistol) and it works perfectly. the moment I added an idle and reload animation the pistol slider stops moving back when recoiling. When I disable the animator component it works back fine, I even deleted all my keyframes on the "idle" animation still the slider doesn't work.

I have my Recoil Script below

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class WeaponRecoil : MonoBehaviour
{
    [SerializeField]
    private float recoilX;
    [SerializeField]
    private float recoilY;
    [SerializeField]
    private float recoilZ;
    [SerializeField]
    private float recoilResetSpeed = 0.8f;
    [SerializeField]
    private float snappiness = 2f;

    public Transform parent;
    Vector3 targetRotation;
    Vector3 currentRotation;
    Vector3 targetHandPosition;
    Vector3 currentHandPosition;
   
    [SerializeField]
    private List<EjectorData> bulletEjectors;
    [SerializeField]
    float ejectorBackDistance;
    [SerializeField]
    float ejectorReturnSpeed=10;

    void Start() {
        foreach (EjectorData ejector in bulletEjectors)
        {
            ejector.startPosition = ejector.obj.localPosition;
        }
    }
    
    void Update()
    {
        coolDownWeaponRecoilEffect();
    }

    void coolDownWeaponRecoilEffect()
    {
        targetRotation = Vector3.Lerp(targetRotation, Vector3.zero, recoilResetSpeed * Time.deltaTime);
        currentRotation = Vector3.Slerp(currentRotation, targetRotation, snappiness * Time.deltaTime);
        //resetting the pushback effect of the recoil of the hand
        targetHandPosition = Vector3.Lerp(targetHandPosition, Vector3.zero, recoilResetSpeed * Time.deltaTime);
        currentHandPosition = Vector3.Slerp(currentHandPosition, targetHandPosition, snappiness * Time.deltaTime);

        if (parent == null) return;
        parent.localRotation = Quaternion.Euler(currentRotation);
        transform.localPosition = currentHandPosition;

        //resseting the bullet ejectors back to their normal positions
        foreach (EjectorData ejector in bulletEjectors)
        {
            ejector.obj.localPosition = Vector3.Lerp(ejector.obj.localPosition, ejector.startPosition, ejectorReturnSpeed * Time.deltaTime);
        }

    }

    public void Recoil() {
        targetRotation += new Vector3(recoilX, Random.Range(-recoilY, recoilY), 0);
        targetHandPosition -= new Vector3(0, 0, recoilZ);
        foreach (EjectorData ejector in bulletEjectors) {
            ejector.obj.localPosition -= new Vector3(0, 0, ejectorBackDistance);
        }
    }

    [System.Serializable]
    class EjectorData {
        public Transform obj;
        public Vector3 startPosition;
    }

}


Solution

  • To put is simply your script is overriding the position of the bones, and the animator is overriding that; rendering it useless

    To change the bones rotation/Position on top the animation you should look into Unity's Animation Rigging

    AnimationRigging works as a post-process step of the Animator and has the same restrictions as any animated hierarchy. you can use the Override Transform Constraint To achieve the effect you are looking for.

    this tutorial might help you a bit if you are stuck.