- Home /
No longer a problem
Enemy AI Problem: Dancing?
Edit: The actual movement of the enemy has now been fixed with this script update. Now, the buggy, dance-like rotation needs to be fixed. Suggestions?
I have created an "AI" script for a third person game that is somewhat functional, but is buggy. I want the enemy to rotate to "look" at the player, go up to the player when the player has come within a certain distance, and attack the player at a certain distance. The enemy rotates properly at a distance, but moves crazily when closer. The movement is like a quick, erotic, jumpy dance that circles the player... It looks cool, but it isn't what I want.
Here is the AI script:
// #pragma strict
// Set variables related to movement and individual states.
var idleTime : float = 3.1;
var marManMoveSpeed : float;
var marManRotateSpeed : float;
var gravity : float;
var attackTarget : Transform;
var chaseDistance : float;
var attackDistance : float;
private var moveDirection : Vector3 = Vector3.zero;
private var wait : boolean = false;
// Set variables for the states.
private var idle : int = 0;
private var walk : int = 1;
private var attack : int = 2;
private var status : int = idle;
var controller : CharacterController;
controller = GetComponent(CharacterController);
function Update(){
var controller : CharacterController = GetComponent(CharacterController);
moveDirection.x = 0;
moveDirection.z = 0;
var aggro : float = Vector3.Distance(attackTarget.position,transform.position);
//print(aggro);
//Define what causes the states.
if (aggro < chaseDistance && aggro > attackDistance){
status = walk;
}
else if(aggro < attackDistance * 0.8){
status = attack;
}
else if(aggro > chaseDistance){
status = idle;
}
//State what to do in each of the states.
if (controller.isGrounded){
switch(status){
case idle:
//Currently does nothing, but will hopefully have animation here.
break;
case walk:
//Insert animation later.
//Rotation handled later in script.
//Set the direction for movement.
moveDirection = Vector3(attackTarget.position.x - transform.position.x,0,attackTarget.position.z - transform.position.z);
moveDirection.Normalize();
break;
case attack:
//Insert "attack" animation later. Required for end use.
//Rotation handled later in script.
break;
default:
return;
}
}
//Always apply gravity.
moveDirection[1] -= gravity * Time.deltaTime;
Move();
}
function Move(){
if(!wait){
//Handle all movement in here.
transform.LookAt(attackTarget);
controller.Move(moveDirection * marManMoveSpeed * Time.deltaTime);
}
else{
//Wait for assigned time and do nothing!
Debug.LogWarning("Current status - waiting for "+idleTime);
yield WaitForSeconds(idleTime);
return;
}
}
@script RequireComponent(CharacterController)
Here are the settings for this script in the inspector:
Idle Time = 3.1
Mar Man Move Speed = 5.291004
Mar Man Rotate Speed = 360
Gravity = 50
Attack Target = Player (Transform)
Chase Distance = 5
Attack Distance = 1.75
Controller = Enemy (Character Controller)
is it possible that the player's Y is lower than the Enemy's Y ? This is just a guess / speculation.
other Qn on the topic : http://answers.unity3d.com/questions/54973/rotate-an-object-to-look-at-another-object-on-one.html
also forgot to suggest that you put your inspector variables in the script :
var idleTime : float = 3.1;
var mar$$anonymous$$an$$anonymous$$oveSpeed : float = 5.3;
var mar$$anonymous$$anRotateSpeed : float = 3.4;
var gravity : float = 50.0;
var attackTarget : Transform;
var chaseDistance : float = 5.0;
var attackDistance : float = 0.25;
you can still change them in the inspector, it just saves putting them in if you put this script on a new gameObject,
The player's Y is lower than the Enemy's Y!
Thanks for the tip!
was just looking and you saw this. Check out the answer on this Qn for rotating in the Y only for look at :
maybe can replace your transform.LookAt(attackTarget); with everything in their Update function (but use your attackTarget ins$$anonymous$$d of their targetPosition )
var targetPoint = attackTarget.position;
var targetRotation = Quaternion.LookRotation(targetPoint - transform.position, Vector.up);
transform.position.y = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime*2.0).y;
Replaced the current "transform.LookAt(attackTarget)" with "transform.Rotate(0,attackTarget.position.y * mar$$anonymous$$anRotateSpeed,0,Space.World);"
Same thing happens, but only faster... Now he's breakdancing!
Edit: Also tried Quaternion functions with the following var targetRotation = Quaternion.LookRotation(attackTarget.position - transform.position, Vector3.up);
transform.rotation.y = Quaternion.Slerp(transform.rotation, targetRotation, mar$$anonymous$$anRotateSpeed * Time.deltaTime).y;
This came from the unity3d.quatohost.com site. I copied the code and changed a few variables to fit my case. No change in reaction from the enemy.
I must be doing something else wrong, too... Ideas?
attempted to limit movement toward the player to the x and z axes by replacing this:
moveDirection = attackTarget.position - transform.position; break;
with this:
moveDirection += Vector3(attackTarget.position.x - transform.position.x,0,attackTarget.position.z - transform.position.z)
The sparatic movement of the enemy has decreased greatly, but still is spinning. $$anonymous$$ovement has been greatly improved, but is still somewhat variable because of spinning.
Answer by Owen-Reynolds · Mar 24, 2012 at 02:01 PM
Your enemy is still moving when it gets within attack range. Sure, you only set moveDirection
when you're walking, but you call Move()
every frame. So, when you're close, you use the old value of moveDirection
, which skitters you a little off to one side.
I'd prefer to split into Move and Turn. Or you could have attack zero-out moveDirection
x and z.
attackDistance
seems pretty small. The measurement is center-to-center, and 0.25 is 10 inches (25 centimeters.) Two standard Unity cubes (for example) are touching when they are 1 unit apart. But if you really are scaled that small (radius 0.08 colliders,) then 0.25 for attackDistance is fine.
When you compute moveDirection
you should probably normalize it. Right now, your speed is based on distance -- you move faster the further you are away. If you raised chase distance to 15, you'd see the enemy blast towards you at triple speed, then slow down.
Another trick is to walk
until you are at maybe 80% of attack range, but don't go from attack
back to walk
until you're out of range. That way the player can shift a little and you won't make unrealistic tiny steps to keep up.
I took some of your suggestions and tested them. I had the x and z values of moveDirection set to zero at the beginning of Update() and normalized moveDirection at the end of Update(). I also changed some of the causes of state change, as suggested. None of these actually fixed the rotation, though, and the enemy now falls more slowly to the ground... I changed the script to reflect these changes. Any suggestions?
Sorry for the confusion -- the comment about 80% distance isn't to fix this problem. It's to fix the next problem you might get once you fix this one.
Normalizing is for just after computing the distance (X $$anonymous$$us X, Z $$anonymous$$us Z.) It turns the line between you two into a standard-sized arrow pointing that way, which you can use to move. Also, normalizing readjusts Y, so add gravity later.
The code should be stopping when it gets near you. Try printing the state. Is it always walk? If so, keep jacking up attackDistance.
Fixed and updated the code. It works fairly great...except for the rotating.
Follow this Question
Related Questions
How to make AI look like swat team members? 3 Answers
AI controller script, Enemy movement issues 0 Answers
Problem with the movement of AI 1 Answer
Rotating on X and Z axis only 2 Answers