0
\$\begingroup\$

First of all, I have a character with 2 armatures (Upper/Lower Body), where the Upper Body is the child of the Lower Body's root bone (Hip). This is intended, and I really don't want to touch the hierarchy, as it would break a lot of other things.

The thing is, that the animator of the Lower Body works like a charm and reacts to SetTrigger as expected (although in a different script, but on the same GameObject), while if I try to do the same for the Upper Body, it just throws me the following error and does nothing:

Parameter 'Attack' does not exist. UnityEngine.Animator:SetTrigger (string)

The Trigger does exist with the proper name (no typo), set up on the right animator with the right transitions. If I trigger it from the animator on spawn, it does play it, so the Trigger does exist.

Here is part of the code:

[SerializeField] GameObject goal;
[SerializeField] Transform stance;
[SerializeField] ParticleSystem projectileParticles;
[SerializeField] float targetRange = 10f;
[SerializeField] float attackRange = 5f;
[SerializeField] Transform enemy;

public GameObject Target;
public float maxDistance;

public NavMeshAgent agent;
public Animator animUpper;
public Animator animLower;

public void Start()
{
    animUpper = GetComponentInChildren<Animator>();
    animLower = GetComponentInChildren<Animator>();
}

void Update()
{
    FindClosestEnemy();
    AttackEnemy();
}

public void FindClosestEnemy()
{
    List<GameObject> nearestEnemies = new List<GameObject>();
    GameObject lastAddedEnemy = null;
    GameObject enemyTemp = null;

    float maxDistance = Mathf.Infinity;
    Vector3 position = transform.position;

    enemyTemp = FindNearestWithTag("RightBrute");
    if (enemyTemp != null && enemyTemp != lastAddedEnemy)
    {
        nearestEnemies.Add(enemyTemp);
        lastAddedEnemy = enemyTemp;
    }

    enemyTemp = FindNearestWithTag("RightRanged");
    if (enemyTemp != null && enemyTemp != lastAddedEnemy)
    {
        nearestEnemies.Add(enemyTemp);
        lastAddedEnemy = enemyTemp;
    }

    enemyTemp = FindNearestWithTag("RightArmor");
    if (enemyTemp != null && enemyTemp != lastAddedEnemy)
    {
        nearestEnemies.Add(enemyTemp);
        lastAddedEnemy = enemyTemp;
    }

    foreach (var enemy in nearestEnemies)
    {
        Vector3 diff = enemy.transform.position - position;
        float currentDistance = diff.sqrMagnitude;
        if (currentDistance < maxDistance)
        {
            enemyTemp = enemy;
            maxDistance = currentDistance;
        }
    }

    Target = enemyTemp;
}

public GameObject FindNearestWithTag(string tagName)
{
    GameObject[] enemies;
    enemies = GameObject.FindGameObjectsWithTag(tagName);
    GameObject closest = null;
    float maxDistance = Mathf.Infinity;
    Vector3 position = transform.position;

    foreach (var enemy in enemies)
    {
        Vector3 diff = enemy.transform.position - position;
        float currentDistance = diff.sqrMagnitude;
        if (currentDistance < maxDistance)
        {
            closest = enemy;
            maxDistance = currentDistance;
        }
    }

    return closest;
}

public void AttackEnemy()
{
    if (Target == null)
    {
        stance.LookAt(goal.transform);
        Attack(false);
        Move();
    }
    else
    {
        float maxDistance = Vector3.Distance(transform.position, Target.transform.position);
        stance.LookAt(Target.transform);

        if (maxDistance < targetRange)
        {
            Approach();

            if (maxDistance < attackRange)
            {
                Attack(true);
                animUpper.SetTrigger("Attack");
                //GetComponentInChildren<Animator>().SetTrigger("Attack");
            }
        }
        else
        {
            stance.LookAt(goal.transform);
            Attack(false);
            Move();
        }
    }
}

public void Attack(bool isActive)
{
    var emissionModule = projectileParticles.emission;
    emissionModule.enabled = isActive;
}

public void Move()
{
    NavMeshAgent agent = GetComponent<NavMeshAgent>();
    agent.SetDestination(goal.transform.position);
    animLower.SetFloat("Speed", agent.velocity.magnitude);
}

public void Approach()
{
    NavMeshAgent agent = GetComponent<NavMeshAgent>();
    agent.SetDestination(Target.transform.position);
    animLower.SetFloat("Speed", agent.velocity.magnitude);
}

As you can see I've tried using GetComponentInChildren().SetTrigger("Attack"); but it resulted in the same error. I've been at it for days now and I'm completely out of ideas. I beg for some help, because the way I see it from other questions, it is a bug with Unity itself, in which case I would really like to get a workaround and a cold compress.

Reproduction steps with example character:

Yellow: Hierarchy; Orange: Script in question; Red: Properly assigned Animators; Yellow: Hierarchy, Orange: Script in question, Red: Properly assigned Animators

Orange: Upper Body Animator; Red: Trigger set up properly; Trigger is set up on two transitions for testing purposes. Changing it doesn't fix anything. I originally wanted it to come from Any State only. Orange: Upper Body Animator, Red: Trigger set up properly

Animated character FBX files: https://we.tl/t-m8Yo1yMttS

Complete, simplified example project: https://we.tl/t-7P3FFwK8tv

To test the issue, just hit Play, go back into Scene and drag the Cube over the character. It gives the exact same results.

\$\endgroup\$
9
  • \$\begingroup\$ Can you share a minimal complete verifiable example of this? Once we can reproduce the problem in a new, empty project, we can test potential fixes. A minimal sample van also be submitted to Unity to help them isolate and fix the bug if it is indeed an engine problem. \$\endgroup\$
    – DMGregory
    Commented Sep 6, 2021 at 19:09
  • \$\begingroup\$ If you drag the animators in the editor on the script, is it working as you want? If yes, your GetComponentInChildren<Animator>() returns twice the same animator \$\endgroup\$
    – Zibelas
    Commented Sep 6, 2021 at 19:34
  • \$\begingroup\$ @DMGregory I'm not sure what you mean. You want a brand new project with the bare minimum of assets required to reproduce, or images/video of the issue? \$\endgroup\$
    – 505Legion
    Commented Sep 6, 2021 at 22:01
  • \$\begingroup\$ @Zibelas They were always dragged on. I tried it without them being assigned to the script, but the results were I believe the same. Also, it's quite unlikely that it would return the same animator twice, since they are pretty much completely separated even in code, as you can see in their names: animUpper, animLower \$\endgroup\$
    – 505Legion
    Commented Sep 6, 2021 at 22:01
  • \$\begingroup\$ What we need is the smallest set of instructions you can give us that would allow us to reproduce this problem ourselves. Once we can reproduce what you're struggling with, we can test potential fixes to be sure they'll work for you. The process of boiling it down to a minimal example also helps eliminate a lot of potential causes or noise that can distract from the root issue, helping you get answers faster. \$\endgroup\$
    – DMGregory
    Commented Sep 6, 2021 at 22:08

1 Answer 1

1
\$\begingroup\$

As I wrote in the comment, you are loading twice the same animator. While you do assign the correct ones in the inspector, in the code you are reloading the animators in the start method. Therefore in your case it would not matter at all what got assigned in the Editor for Upper/ Lower Body, it gets overwritten afterwards.

GetComponentInChildren<Animator>() returns you the first animator it finds. Calling it again returns the same again.

To fix your problem, either just delete the two lines (since you assign it over the inspector) or use a different loading. GetComponentsInChildren would return you all animators, you would need to know in which order they get returned.

On a side note, you might want to reconsider FindGameObjectsWithTag the way you use it right now for finding your enemies. It is likely that you are going to run into performance issues, it is not recommended to call it in Update and not even needed. In the example code you posted, you could load once at Start all enemies and just iterate over the list as you do now and compare to the tag.

\$\endgroup\$
1
  • \$\begingroup\$ Thank you so much! I honestly didn't think it would be returned twice, since it just didn't make sense for me. Sorry if I came out arrogant for thinking that way in my comment. You've done me a great service, thank you again! \$\endgroup\$
    – 505Legion
    Commented Sep 7, 2021 at 10:54

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .