- Home /
Enemy Ai is not working as planned
Hey guys i have this script attatched to an enemy but it is not working as i wanted to...when the character begins to attack he never stops even if my character is far away...what am i doing wrong? This is the script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BabyAi : MonoBehaviour
{
//Inspector initiated variables. Defaults are set for ease of use.
public bool on = true; //Is the AI active? this can be used to place pre-set enemies in you scene.
public bool canFly = false; //Flying alters float behavior to ignore gravity. The enemy will fly up or down only to sustain floatHeight level.
public float floatHeight = 0.0f; //If it can fly/hover, you need to let the AI know how high off the ground it should be.
public bool runAway = false; //Is it the goal of this AI to keep it's distance? If so, it needs to have runaway active.
public bool runTo = false; //Opposite to runaway, within a certain distance, the enemy will run toward the target.
public float runDistance = 25.0f; //If the enemy should keep its distance, or charge in, at what point should they begin to run?
public float runBufferDistance = 50.0f; //Smooth AI buffer. How far apart does AI/Target need to be before the run reason is ended.
public int walkSpeed = 10; //Standard movement speed.
public int runSpeed = 15; //Movement speed if it needs to run.
public int randomSpeed = 10; //Movement speed if the AI is moving in random directions.
public float rotationSpeed = 20.0f; //Rotation during movement modifier. If AI starts spinning at random, increase this value. (First check to make sure it's not due to visual radius limitations)
public float visualRadius = 100.0f; //How close does the player need to be to be seen by the enemy? Set to 0 to remove this limitation.
public float moveableRadius = 200.0f; //If the player is too far away, the AI will auto-matically shut down. Set to 0 to remove this limitation.
public float attackRange = 10.0f; //How close does the enemy need to be in order to attack?
public float attackTime = 0.50f; //How frequent or fast an enemy can attack (cool down time).
public bool useWaypoints = false; //If true, the AI will make use of the waypoints assigned to it until over-ridden by another functionality.
public bool reversePatrol = true; //if true, patrol units will walk forward and backward along their patrol.
public Transform[] waypoints; //define a set path for them to follow.
public bool pauseAtWaypoints = false; //if true, patrol units will pause momentarily at each waypoint as they reach them.
public float pauseMin = 1.0f; //If pauseAtWaypoints is true, the unit will pause momentarily for minmum of this time.
public float pauseMax = 3.0f; //If pauseAtWaypoints is true, the unit will pause momentarily formaximum of this time.
public float huntingTimer = 5.0f; //Search for player timer in seconds. Minimum of 0.1
public bool estimateElevation = false; //This implements a pause between raycasts for heights and guestimates the need to move up/down in height based on the previous raycast.
public float estRayTimer = 1.0f; //The amount of time in seconds between raycasts for gravity and elevation checks.
public bool requireTarget = true; //Waypoint ONLY functionality (still can fly and hover).
public Transform target; //The target, or whatever the AI is looking for.
Animator anim;
//private script handled variables
private bool initialGo = false; //AI cannot function until it is initialized.
private bool go = true; //An on/off override variable
private Vector3 lastVisTargetPos; //Monitor target position if we lose sight of target. provides semi-intelligent AI.
public GameObject gameobject; //CC used for enemy movement and etc.
private bool playerHasBeenSeen = false; //An enhancement to how the AI functions prior to visibly seeing the target. Brings AI to life when target is close, but not visible.
private bool enemyCanAttack = false; //Used to determine if the enemy is within range to attack, regardless of moving or not.
private bool enemyIsAttacking = false; //An attack interuption method.
private bool executeBufferState = false; //Smooth AI buffer for runAway AI. Also used as a speed control variable.
private bool walkInRandomDirection = false; //Speed control variable.
private float lastShotFired; //Used in conjuction with attackTime to monitor attack durations.
private float lostPlayerTimer; //Used for hunting down the player.
private bool targetIsOutOfSight; //Player tracking overload prevention. Makes sure we do not call the same coroutines over and over.
private Vector3 randomDirection; //Random movement behaviour setting.
private float randomDirectionTimer; //Random movement behaviour tracking.
private float gravity = 20.0f; //force of gravity pulling the enemy down.
private float antigravity = 2.0f; //force at which floating/flying enemies repel
private float estHeight = 0.0f; //floating/flying creatures using estimated elevation use this to estimate height necessities and gravity impacts.
private float estGravityTimer = 0.0f; //floating/flying creatures using estimated elevation will use this to actually monitor time values.
private int estCheckDirection = 0; //used to determine if AI is falling or not when estimating elevation.
private bool wpCountdown = false; //used to determine if we're moving forward or backward through the waypoints.
private bool monitorRunTo = false; //when AI is set to runTo, they will charge in, and then not charge again to after far enough away.
private int wpPatrol = 0; //determines what waypoint we are heading toward.
private bool pauseWpControl; //makes sure unit pauses appropriately.
private bool smoothAttackRangeBuffer = false; //for runAway AI to not be so messed up by their visual radius and attack range.
//---Starting/Initializing functions---//
void Start()
{
anim = GetComponent<Animator>();
anim.SetBool("isWalking", true);
StartCoroutine(Initialize()); //co-routine is used incase you need to interupt initiialization until something else is done.
}
IEnumerator Initialize()
{
if ((estimateElevation) && (floatHeight > 0.0f))
{
estGravityTimer = Time.time;
}
initialGo = true;
yield return null;
}
//---Main Functionality---//
void Update()
{
if (!on || !initialGo)
{
return;
}
else
{
AIFunctionality();
}
}
void AIFunctionality()
{
if ((!target) && (requireTarget))
{
return; //if no target was set and we require one, AI will not function.
}
//Functionality Updates
lastVisTargetPos = target.position; //Target tracking method for semi-intelligent AI
Vector3 moveToward = lastVisTargetPos - transform.position; //Used to face the AI in the direction of the target
Vector3 moveAway = transform.position - lastVisTargetPos; //Used to face the AI away from the target when running away
float distance = Vector3.Distance(transform.position, target.position);
if (go)
{
MonitorGravity();
}
if (!requireTarget)
{
//waypoint only functionality
Patrol();
}
else if (TargetIsInSight())
{
if (!go)
{ //useWaypoints is false and the player has exceeded moveableRadius, shutdown AI until player is near.
return;
}
if ((distance > attackRange) && (!runAway) && (!runTo))
{
enemyCanAttack = false; //the target is too far away to attack
MoveTowards(moveToward); //move closer
}
else if ((smoothAttackRangeBuffer) && (distance > attackRange + 5.0f))
{
smoothAttackRangeBuffer = false;
WalkNewPath();
}
else if ((runAway || runTo) && (distance > runDistance) && (!executeBufferState))
{
//move in random directions.
if (monitorRunTo)
{
monitorRunTo = false;
}
if (runAway)
{
WalkNewPath();
}
else
{
MoveTowards(moveToward);
}
}
else if ((runAway || runTo) && (distance < runDistance) && (!executeBufferState))
{ //make sure they do not get too close to the target
//AHH! RUN AWAY!... or possibly charge :D
anim = GetComponent<Animator>();
anim.SetBool("isAttacking", false);
enemyCanAttack = false; //can't attack, we're running!
if (!monitorRunTo)
{
executeBufferState = true; //smooth buffer is now active!
}
walkInRandomDirection = false; //obviously we're no longer moving at random.
if (runAway)
{
MoveTowards(moveAway); //move away
}
else
{
MoveTowards(moveToward); //move toward
}
}
else if (executeBufferState && ((runAway) && (distance < runBufferDistance)) || ((runTo) && (distance > runBufferDistance)))
{
//continue to run!
if (runAway)
{
MoveTowards(moveAway); //move away
}
else
{
MoveTowards(moveToward); //move toward
}
}
else if ((executeBufferState) && (((runAway) && (distance > runBufferDistance)) || ((runTo) && (distance < runBufferDistance))))
{
monitorRunTo = true; //make sure that when we have made it to our buffer distance (close to user) we stop the charge until far enough away.
executeBufferState = false; //go back to normal activity
}
//start attacking if close enough
if ((distance < attackRange) || ((!runAway && !runTo) && (distance < runDistance)))
{
if (runAway)
{
smoothAttackRangeBuffer = true;
}
if (Time.time > lastShotFired + attackTime)
{
StartCoroutine(Attack());
}
}
}
else if ((playerHasBeenSeen) && (!targetIsOutOfSight) && (go))
{
lostPlayerTimer = Time.time + huntingTimer;
StartCoroutine(HuntDownTarget(lastVisTargetPos));
}
else if (useWaypoints)
{
Patrol();
}
else if (((!playerHasBeenSeen) && (go)) && ((moveableRadius == 0) || (distance < moveableRadius)))
{
//the idea here is that the enemy has not yet seen the player, but the player is fairly close while still not visible by the enemy
//it will move in a random direction continuously altering its direction every 2 seconds until it does see the player.
WalkNewPath();
}
}
//attack stuff...
IEnumerator Attack()
{
anim.SetBool("isWalking", false);
enemyCanAttack = true;
anim.SetBool("isAttacking", true);
if (!enemyIsAttacking)
{
enemyIsAttacking = true;
while (enemyCanAttack)
{
lastShotFired = Time.time;
//implement attack variables here
yield return new WaitForSeconds(attackTime);
}
}
}
//----Helper Functions---//
//verify enemy can see the target
bool TargetIsInSight()
{
//determine if the enemy should be doing anything other than standing still
if ((moveableRadius > 0) && (Vector3.Distance(transform.position, target.position) > moveableRadius))
{
go = false;
}
else
{
go = true;
}
//then lets make sure the target is within the vision radius we allowed our enemy
//remember, 0 radius means to ignore this check
if ((visualRadius > 0) && (Vector3.Distance(transform.position, target.position) > visualRadius))
{
return false;
}
//Now check to make sure nothing is blocking the line of sight
RaycastHit sight;
if (Physics.Linecast(transform.position, target.position, out sight))
{
if (!playerHasBeenSeen && sight.transform == target)
{
playerHasBeenSeen = true;
}
return sight.transform == target;
}
else
{
return false;
}
}
//target tracking
IEnumerator HuntDownTarget(Vector3 position)
{
//if this function is called, the enemy has lost sight of the target and must track him down!
//assuming AI is not too intelligent, they will only move toward his last position, and hope they see him
//this can be fixed later to update the lastVisTargetPos every couple of seconds to leave some kind of trail
targetIsOutOfSight = true;
while (targetIsOutOfSight)
{
Vector3 moveToward = position - transform.position;
MoveTowards(moveToward);
//check if we found the target yet
if (TargetIsInSight())
{
targetIsOutOfSight = false;
break;
}
//check to see if we should give up our search
if (Time.time > lostPlayerTimer)
{
targetIsOutOfSight = false;
playerHasBeenSeen = false;
break;
}
yield return null;
}
}
void Patrol()
{
if (pauseWpControl)
{
return;
}
Vector3 destination = CurrentPath();
anim.SetBool("isWalking", true);
Vector3 moveToward = destination - transform.position;
float distance = Vector3.Distance(transform.position, destination);
MoveTowards(moveToward);
if (distance <= 1.5f + floatHeight)
{// || (distance < floatHeight+1.5f)) {
if (pauseAtWaypoints)
{
if (!pauseWpControl)
{
pauseWpControl = true;
StartCoroutine(WaypointPause());
}
}
else
{
NewPath();
}
}
}
IEnumerator WaypointPause()
{
anim.SetBool("isWalking", false);
yield return new WaitForSeconds(Random.Range(pauseMin, pauseMax));
NewPath();
pauseWpControl = false;
}
Vector3 CurrentPath()
{
return waypoints[wpPatrol].position;
}
void NewPath()
{
if (!wpCountdown)
{
wpPatrol++;
if (wpPatrol >= waypoints.GetLength(0))
{
if (reversePatrol)
{
wpCountdown = true;
wpPatrol -= 2;
}
else
{
wpPatrol = 0;
}
}
}
else if (reversePatrol)
{
wpPatrol--;
if (wpPatrol < 0)
{
wpCountdown = false;
wpPatrol = 1;
}
}
}
//random movement behaviour
void WalkNewPath()
{
if (!walkInRandomDirection)
{
walkInRandomDirection = true;
if (!playerHasBeenSeen)
{
randomDirection = new Vector3(Random.Range(-0.15f, 0.15f), 0, Random.Range(-0.15f, 0.15f));
}
else
{
randomDirection = new Vector3(Random.Range(-0.5f, 0.5f), 0, Random.Range(-0.5f, 0.5f));
}
randomDirectionTimer = Time.time;
}
else if (walkInRandomDirection)
{
MoveTowards(randomDirection);
}
if ((Time.time - randomDirectionTimer) > 2)
{
//choose a new random direction after 2 seconds
walkInRandomDirection = false;
}
}
//standard movement behaviour
void MoveTowards(Vector3 direction)
{
direction.y = 0;
int speed = walkSpeed;
if (walkInRandomDirection)
{
speed = randomSpeed;
}
if (executeBufferState)
{
speed = runSpeed;
}
//rotate toward or away from the target
transform.rotation = Quaternion.Slerp(transform.rotation, Quaternion.LookRotation(direction), rotationSpeed * Time.deltaTime);
transform.eulerAngles = new Vector3(0, transform.eulerAngles.y, 0);
//slow down when we are not facing the target
Vector3 forward = transform.TransformDirection(Vector3.forward);
float speedModifier = Vector3.Dot(forward, direction.normalized);
speedModifier = Mathf.Clamp01(speedModifier);
//actually move toward or away from the target
direction = forward * speed * speedModifier;
if ((!canFly) && (floatHeight <= 0.0f))
{
direction.y -= gravity;
}
gameobject.transform.Translate(direction * Time.deltaTime);
anim.SetBool("isWalking", true);
}
//continuous gravity checks
void MonitorGravity()
{
Vector3 direction = new Vector3(0, 0, 0);
if ((!canFly) && (floatHeight > 0.0f))
{
//we need to make sure our enemy is floating.. using evil raycasts! bwahahahah!
if ((estimateElevation) && (estRayTimer > 0.0f))
{
if (Time.time > estGravityTimer)
{
RaycastHit floatCheck;
if (Physics.Raycast(transform.position, -Vector3.up, out floatCheck))
{
if (floatCheck.distance < floatHeight - 0.5f)
{
estCheckDirection = 1;
estHeight = floatHeight - floatCheck.distance;
}
else if (floatCheck.distance > floatHeight + 0.5f)
{
estCheckDirection = 2;
estHeight = floatCheck.distance - floatHeight;
}
else
{
estCheckDirection = 3;
}
}
else
{
estCheckDirection = 2;
estHeight = floatHeight * 2;
}
estGravityTimer = Time.time + estRayTimer;
}
switch (estCheckDirection)
{
case 1:
direction.y += antigravity;
estHeight -= direction.y * Time.deltaTime;
break;
case 2:
direction.y -= gravity;
estHeight -= direction.y * Time.deltaTime;
break;
default:
//do nothing
break;
}
}
else
{
RaycastHit floatCheck;
if (Physics.Raycast(transform.position, -Vector3.up, out floatCheck, floatHeight + 1.0f))
{
if (floatCheck.distance < floatHeight)
{
direction.y += antigravity;
}
}
else
{
direction.y -= gravity;
}
}
}
else
{
//bird like creature! Again with the evil raycasts! :p
if ((estimateElevation) && (estRayTimer > 0.0f))
{
if (Time.time > estGravityTimer)
{
RaycastHit floatCheck;
if (Physics.Raycast(transform.position, -Vector3.up, out floatCheck))
{
if (floatCheck.distance < floatHeight - 0.5f)
{
estCheckDirection = 1;
estHeight = floatHeight - floatCheck.distance;
}
else if (floatCheck.distance > floatHeight + 0.5f)
{
estCheckDirection = 2;
estHeight = floatCheck.distance - floatHeight;
}
else
{
estCheckDirection = 3;
}
}
estGravityTimer = Time.time + estRayTimer;
}
switch (estCheckDirection)
{
case 1:
direction.y += antigravity;
estHeight -= direction.y * Time.deltaTime;
break;
case 2:
direction.y -= antigravity;
estHeight -= direction.y * Time.deltaTime;
break;
default:
//do nothing
break;
}
}
else
{
RaycastHit floatCheck;
if (Physics.Raycast(transform.position, -Vector3.up, out floatCheck))
{
if (floatCheck.distance < floatHeight - 0.5f)
{
direction.y += antigravity;
}
else if (floatCheck.distance > floatHeight + 0.5f)
{
direction.y -= antigravity;
}
}
}
}
if ((!estimateElevation) || ((estimateElevation) && (estHeight >= 0.0f)))
{
gameobject.transform.Translate(direction * Time.deltaTime);
}
}
}
Comment
Your answer
Follow this Question
Related Questions
Multiple Cars not working 1 Answer
Distribute terrain in zones 3 Answers
Issue with mecanim playing an animation using setbool 1 Answer
Built project, now scripts are missing. 2 Answers
video is not playing; Using Url 0 Answers