Enemy leans back and jitters when in attack range.

Quick question for all of you out there. I was following a tutorial for a rudimentary enemy ai in Unity, and the rest of the code seems to work fine on my end.

However, the segment for attacking the character seems to make the capsule collider of the enemy and its child GFX model angle back 10 degrees and jitter once in range, the angle getting worse when approaching. Changing the height of the collider doesn’t seem to help, so I thought I’d ask before going further. For reference, the following snippet is the offending code, since turning it off fixes the problem (albeit the enemy doesn’t work, but that’s besides the point).

Everything else works as intended, even if it’s just the basics.

I’m just starting out and though pride kept me from here earlier, the going has been slow so far, so any help would be appreciated.

using UnityEngine;
using UnityEngine.AI;

public class EnemyController : MonoBehaviour
{
	public float lookRadius = 10f;
	public Transform player;// The target of the AI
	public NavMeshAgent agent;// Some sort of thing to make the ai understand how to move around an area
	public LayerMask whatIsGround, whatIsPlayer;// What will tell the AI if it is walking on a proper surface, and who the target is
	public float health = 50f;//self explanatory

	//Patrolling
	public Vector3 walkPoint;
	bool walkPointSet;
	public float walkPointRange;

	//Attack Variables (Whatever these are called again)
	public float timeBetweenAttacks;
	bool alreadyAttacked;
	public GameObject projectile;

	//States
	public float sightRange, attackRange;
	public bool playerInAttackRange, playerInSightRange, playerInLineOfSight;
	bool lineOfSight;

	private void Awake()
	{
		player = GameObject.Find("PlayerCamera").transform;
		agent = GetComponent<NavMeshAgent>();
	}

	private void Update()
	{
		agent.updateRotation = true;

		//Check for Sight and Attack Range
		playerInAttackRange = Physics.CheckSphere(transform.position, attackRange, whatIsPlayer);
		playerInSightRange = Physics.CheckSphere(transform.position, sightRange, whatIsPlayer);

		if (!playerInSightRange && !playerInAttackRange) Idle();
		if (playerInSightRange && !playerInAttackRange)  MoveInOnPlayer();
		if (playerInSightRange && playerInAttackRange)   AttackPlayer();
		//if (!playerInLineOfSight && playerInAttackRange) FlankPlayer();
	}

	void Idle()
	{
		if (!walkPointSet) SearchWalkPoint();

		if (walkPointSet)
			agent.SetDestination(walkPoint);

		Vector3 distanceToWalkPoint = transform.position - walkPoint;

		if (distanceToWalkPoint.magnitude < 1f)
			walkPointSet = false;
		//They'll always be in sight range, so no need for this
	}

	private void SearchWalkPoint()
	{
		//calculate random point in range
		float randomZ = Random.Range(-walkPointRange, walkPointRange);
		float randomX = Random.Range(-walkPointRange, walkPointRange);

		walkPoint = new Vector3(transform.position.x + randomX, transform.position.y, transform.position.z + randomZ);

		if (Physics.Raycast(walkPoint, -transform.up, 2f, whatIsGround))
			walkPointSet = true;
	}

	void MoveInOnPlayer() { agent.SetDestination(player.position); }

	void AttackPlayer()
	{
		//agent.updateRotation = false;
		transform.LookAt(player.transform);

		agent.SetDestination(transform.position);
		// Right now we follow the guide, so the enemy is stationary here
		//who wants an enemy that doesnt look at you?
		Debug.Log(agent.transform.position);
		if (!alreadyAttacked)
		{
			//Attack code here
			Rigidbody rb = Instantiate(projectile, transform.position, Quaternion.identity).GetComponent<Rigidbody>();
			rb.AddForce(transform.forward * 32f, ForceMode.Impulse);

			///
			alreadyAttacked = true;
			Invoke(nameof(ResetAttack), timeBetweenAttacks);
		}
	}

	void ResetAttack()
	{
		alreadyAttacked = false;
	}
	
}

Changing transform.rotation while NavMeshAgent.updateRotation is enabled causes race condition.

race condition - a situation where 2 scripts have overlapping responsibilities but produce different outputs.

What you want to do is this:

agent.updateRotation = false;
transform.LookAt( player.transform );

and enable updateRotation back again, later on.

My initial supposition were right albeit totally unspecific. The jitter is caused by RigidBody and CharacterController components being on the same gameObject. This results in a race condition (“fight” between the two).

Solution: remove RigidBody component.