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.
I'm assuming I should set it to true in the main script, so that when it's done with this function it turns it back on?
You probably want to enable it back once you are no longer interested in changing it's rotation manually (when player is out of sight, for example)
Answer by andrew-lukasik · Jun 01 at 07:32 PM
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.
On the player? That solved the jitter, thankfully. Now I just need to stop the object leaning back. Freezing the rotation on the rigidbody on the enemy model doesn't seem to work. Any input on that last one?
Also, why do they cause a race condition? Does the thing that searches for the player target use both components?
It still leans back, but I'd rather not take any more of your time. Is there a good source you could point me to, so I can try and fix the rest myself?
Good question. Jitter is caused by bothAlso, why do they cause a race condition?
RigidBody
and
CharacterController
moving this object but on different time steps.
RigidBody
moves your object on
FixedUpdate
(every
0.02
[s]) while
CharacterController
moves it on
Update
(every
1/FPS
[s] so it varies all the time). Which one of them wins? Well, winner is totally random in this setup. So you see these components winning and losing the control at random, every frame.
Your answer
