I’ve created a basic AI that does several things:
- Wanders around the scene
- Walks over bridges whenever they interact with them
- Attacks enemy AI (and the player if enemy) when they get within certain range
- Joins up with other teammate AI, picks a leader, and follows that leader
Among other things. All of this worked, but I realized that my map was way too big and once enemies were defeated, the game got pretty boring very fast. So I decided to create a smaller map. Unfortunately, now that the map is smaller, the AI is completely glitching out. For some reason, gravity no longer effects them (they are moved with a character controller) they glitch out when attacking other AI’s, they don’t join up with teams correctly…
…I’ve had this problem before but only when the AI’s spawned within range of one another, and not to this extreme in which 75% of the code is glitching out. My question is why? Here are the three codes that run the AI:
Controller/Movement
[System.Serializable]
public class Bridge {
public string bridgeName = "Bridge";
public Transform bridge;
public Transform[] mainNodes = new Transform[2];
}
public class StudentController : MonoBehaviour {
private bool on;
public bool onPlayerTeam;
public float speed = 10;
public float runningSpeed;
public float origSpeed;
public float rotateSpeed = 3;
public float acceleration = 1;
public float sightRange = 10;
public float attackRange = 1.0F;
public float rangedBuffer = 30F;
private float attackBuffer;
public bool running = false;
public bool crawling = false;
public bool canRun = true;
private bool finish = true;
public float minWalkTime = 3;
public float maxWalkTime = 10;
public float minRotateAmount = -60;
public float maxRotateAmount = 60;
private float timeToNewDir = 0;
private Vector3 curEuler;
private float yAngle;
public int friendCount;
public float maxDistanceFromLeader;
public Transform leader;
public List<Transform> friends = new List<Transform>();
private bool isLeader;
public int rank = 0;
public Transform target;
private Transform student;
public List<Bridge> bridges = new List<Bridge>(3);
private RaycastHit hit;
private RaycastHit bHit;
private RaycastHit cHit;
private RaycastHit dHit;
private CharacterController controller;
private Squad squadInfo;
private StudentAttack attackInfo;
void Awake () {
student = transform;
controller = student.GetComponent<CharacterController>();
squadInfo = student.parent.GetComponent<Squad>();
attackInfo = student.GetComponent<StudentAttack>();
attackBuffer = attackRange - 0.5F;
curEuler = student.eulerAngles;
yAngle = curEuler.y;
}
void Start () {
origSpeed = speed;
rank = Random.Range(0, friendCount + 1);
finish = true;
Invoke("Run", 5);
}
void Run () {
on = true;
}
void Update () {
if(!on) return;
Search();
if(finish){
MoveToBridge();
}
}
void Search () {
if(!target){
Wander();
}else{
Encounter();
}
if(!leader){
foreach(Transform friend in friends){
float distance = Vector3.Distance(student.position, friend.position);
if(onPlayerTeam){
if(friend.CompareTag("Player") distance <= sightRange){
leader = friend;
student.LookAt(friend);
Vector3 eulerAngles = student.rotation.eulerAngles;
eulerAngles = new Vector3(0, eulerAngles.y, 0);
student.rotation = Quaternion.Euler(eulerAngles);
student.Translate(Vector3.forward * speed/1.5F * Time.deltaTime);
}
}else{
StudentController friendScript = friend.GetComponent<StudentController>();
if(distance <= sightRange){
if(friendScript.leader != student){
student.LookAt(friend);
Vector3 eulerAngles = student.rotation.eulerAngles;
eulerAngles = new Vector3(0, eulerAngles.y, 0);
student.rotation = Quaternion.Euler(eulerAngles);
student.Translate(Vector3.forward * speed/1.5F * Time.deltaTime);
}
if(distance < maxDistanceFromLeader){
if(Random.value < 0.5F){
friendScript.leader = student;
}else{
leader = friend;
}
}
}
}
}
}else{
Follow();
}
}
void Wander () {
if(!leader){
curEuler.y = Mathf.Lerp(curEuler.y, yAngle, rotateSpeed * Time.deltaTime);
student.eulerAngles = curEuler;
float idleTime = Random.Range(minWalkTime, maxWalkTime);
if(Time.time > timeToNewDir){
timeToNewDir = Time.time + idleTime;
float rotateAmount = Random.Range(minRotateAmount, maxRotateAmount);
yAngle = (yAngle + rotateAmount) % 360;
}
var fwd = student.TransformDirection(Vector3.forward);
controller.SimpleMove(fwd * speed);
FindClosestEnemy();
if(target){
Encounter();
}
}
}
void Encounter () {
float playDis = Vector3.Distance(target.position, student.position);
if(playDis < sightRange playDis >= 3 target){
attackInfo.target = target;
if(target.CompareTag("Player")){
PlayerAttack pAt = target.GetComponent<PlayerAttack>();
if(pAt.dueling){
speed = origSpeed/Vector3.Distance(target.position, student.position);
canRun = false;
}else{
speed = origSpeed;
canRun = true;
}
}else{
StudentAttack sAt = target.GetComponent<StudentAttack>();
if(sAt){
if(sAt.dueling){
speed = origSpeed/Vector3.Distance(target.position, student.position);
canRun = false;
}else{
speed = origSpeed;
canRun = true;
}
}
}
Vector3 playVec = target.position - student.position;
var rotate = Quaternion.LookRotation(playVec);
student.rotation = Quaternion.Slerp(student.rotation, rotate, rotateSpeed);
var fwd = student.TransformDirection(Vector3.forward);
if(playDis > sightRange/2 canRun){
running = true;
if(speed < runningSpeed){
speed += acceleration * Time.deltaTime;
speed = Mathf.Clamp(speed, 0, runningSpeed);
}
}else{
running = false;
speed -= acceleration * Time.deltaTime;
speed = Mathf.Clamp(speed, origSpeed, runningSpeed);
}
if(playDis > attackBuffer){
controller.SimpleMove(fwd * speed);
}
}
}
void FindClosestEnemy () {
List<Transform> enemies = attackInfo.enemies;
List<Transform> targeted = squadInfo.targetedEnemies;
float closeDis = Mathf.Infinity;
foreach(Transform enemy in enemies){
float playDis = Vector3.Distance(enemy.position, student.position);
if(playDis < closeDis){
closeDis = playDis;
if(closeDis < sightRange !targeted.Contains(enemy)){
target = enemy;
}
}
}
}
void Follow () {
if(!leader) return;
Vector3 newDir = new Vector3(0, -0.5F, 1);
Debug.DrawRay(student.position, newDir * 10, Color.green);
if(Physics.Raycast(student.position, newDir, out cHit) cHit.collider.CompareTag("Water")){
student.LookAt(leader);
}
if(target){
Encounter();
}else{
FindClosestEnemy();
float idleTime = Random.Range(minWalkTime, maxWalkTime);
float distance = Vector3.Distance(leader.position, student.position);
bool walkBack = false;
if(distance > maxDistanceFromLeader){
timeToNewDir = Time.time + idleTime;
walkBack = true;
}
if(walkBack){
TurnBack(distance, walkBack);
}else{
if(Time.time > timeToNewDir){
timeToNewDir = Time.time + idleTime;
float rotateAmount = Random.Range(minRotateAmount, maxRotateAmount);
yAngle = (yAngle + rotateAmount) % 360;
}
var fwd = student.TransformDirection(Vector3.forward);
controller.SimpleMove(fwd * speed);
curEuler.y = Mathf.Lerp(curEuler.y, yAngle, rotateSpeed * Time.deltaTime);
student.eulerAngles = curEuler;
}
if(!leader.CompareTag("Player")){
StudentController friendScript = leader.GetComponent<StudentController>();
Transform leaderLead = friendScript.leader;
if(leaderLead){
leader = leaderLead;
}
}
}
}
void TurnBack (float distance, bool walk) {
student.LookAt(leader);
var fwd = student.TransformDirection(Vector3.forward);
controller.SimpleMove(fwd * speed);
if(distance < 3){
walk = false;
}
}
public Transform FindNearestObject (string objectTag) {
GameObject[] objects = GameObject.FindGameObjectsWithTag(objectTag);
float closeDis = Mathf.Infinity;
Transform closest = null;
foreach(GameObject obj in objects){
Transform tObj = obj.transform;
float dis = Vector3.Distance(tObj.position, student.position);
if(dis < closeDis){
closest = tObj;
}
}
return(closest);
}
void MoveToBridge () {
Bridge bridge = null;
Vector3 firstPos = new Vector3(0,0,0);
Vector3 secondPos = new Vector3(0,0,0);
bool moveOn = false;
foreach(Bridge bridgeD in bridges){
if(bridgeD.bridge == FindNearestObject("Bridge").transform){
bridge = bridgeD;
}
}
float close = Mathf.Infinity;
foreach(Transform node in bridge.mainNodes){
if(Vector3.Distance(node.position, student.position) < close){
firstPos = node.position;
}else{
secondPos = node.position;
}
}
student.Translate(Vector3.forward * speed * Time.deltaTime);
if(student.position == firstPos){
moveOn = true;
}
if(moveOn){
student.LookAt(secondPos);
}else{
student.LookAt(firstPos);
}
if(student.position == secondPos){
finish = true;
}
}
void OnTriggerEnter (Collider collider) {
if(collider.CompareTag("Boundaries")){
float rotateAmount = Random.Range(180, 360);
yAngle = (yAngle + rotateAmount) % 360;
Debug.Log(student.name + " is near edge");
}
if(collider.CompareTag("Bridge")){
finish = false;
}
}
}
Health, Energy, Stats
public Texture studentIcon;
public bool onPlayerTeam = false;
public string[] enemyTags = new string[2];
public int blockChance = 3;
public bool block;
public float health = 100;
public float energy = 100;
public int maxHealth = 100;
public int maxEnergy = 100;
public bool canRun;
public int runningDegenRate = 10;
public float healthRegenRate = 1;
private bool healthRegen = true;
private bool energyRegen = true;
public float energyRegenRate = 1;
public GameObject healthPack;
public int healthPacks = 2;
public GameObject foodPack;
public int foodPacks = 1;
public float maxSpawnDistance;
private bool lightChange;
private Light powerLight;
private float hpChangeTime;
private bool canUseHP;
private float fpChangeTime;
private bool canUseFP;
private Transform student;
private Squad squadInfo;
private StudentController controller;
void Awake () {
student = transform;
}
void Start () {
squadInfo = student.parent.GetComponent<Squad>();
controller = student.GetComponent<StudentController>();
if(Random.value > 0.5F){
canUseFP = true;
}else{
canUseFP = false;
}
if(Random.value > 0.5F){
canUseHP = true;
}else{
canUseHP = false;
}
}
void LateUpdate () {
bool running = controller.running;
bool crawling = controller.crawling;
if(lightChange){
powerLight.intensity -= 2 * Time.deltaTime;
if(powerLight.intensity <= 0){
lightChange = false;
Destroy(powerLight.gameObject);
powerLight = null;
}
}
energy = Mathf.Clamp(energy, 0, maxEnergy);
health = Mathf.Clamp(health, -10, maxHealth);
if(healthRegen){
health += healthRegenRate * Time.deltaTime;
}
if(running){
energy -= runningDegenRate * Time.deltaTime;
}else if(crawling energyRegen){
energy += (energyRegenRate/2) * Time.deltaTime;
}else if(energy < maxEnergy energyRegen){
energy += energyRegenRate * Time.deltaTime;
}
if(energy <= 0){
canRun = false;
}
if(canRun == false energy > (maxEnergy/12)){
canRun = true;
}
if(hpChangeTime <= 5){
hpChangeTime += Time.deltaTime;
}else{
ChangeStatus(canUseHP, hpChangeTime);
}
if(fpChangeTime <= 5){
fpChangeTime += Time.deltaTime;
}else{
ChangeStatus(canUseFP, fpChangeTime);
}
if(health < (maxHealth/2 - Random.Range(0, 20)) healthPacks > 0 canUseHP){
animation.Stop();
animation.Play("Heal");
health += 25;
healthPacks--;
}
if(energy < (maxEnergy/2 - Random.Range(0, 20)) foodPacks > 0){
animation.Stop();
animation.Play("Eat");
energy += 25;
foodPacks--;
}
}
void ChangeStatus (bool changeVar, float timeVar) {
timeVar = 0.0F;
if(Random.value > 0.5F){
changeVar = true;
}else{
changeVar = false;
}
}
void OnTriggerEnter (Collider collider) {
if(collider.CompareTag("Energy Powerup")){
maxEnergy += 25;
energy += maxEnergy/2;
energy = Mathf.Clamp(energy, 0, maxEnergy);
collider.collider.enabled = false;
powerLight = collider.light;
lightChange = true;
}
if(collider.CompareTag("Health Powerup")){
maxHealth += 25;
health += maxHealth/2;
health = Mathf.Clamp(health, 0, maxHealth);
collider.collider.enabled = false;
powerLight = collider.light;
lightChange = true;
}
if(collider.CompareTag("Enhancer")){
health = maxHealth;
energy = maxEnergy;
controller.speed++;
controller.origSpeed++;
controller.rotateSpeed++;
collider.collider.enabled = false;
powerLight = collider.light;
lightChange = true;
}
if(collider.CompareTag("Power Powerup")){
maxEnergy += 25;
maxHealth += 25;
energy += maxEnergy/2;
health += maxHealth/2;
energy = Mathf.Clamp(energy, 0, maxEnergy);
health = Mathf.Clamp(health, 0, maxHealth);
collider.collider.enabled = false;
powerLight = collider.light;
lightChange = true;
}
if(collider.CompareTag("Health Pack")){
healthPacks++;
Destroy(collider.gameObject);
}
if(collider.CompareTag("Food Pack")){
foodPacks++;
Destroy(collider.gameObject);
}
}
void OnTriggerStay (Collider collider) {
if(collider.CompareTag("Attack Trigger")){
if(collider.transform.parent.CompareTag("Player") !onPlayerTeam){
PlayerAttack playerAttack = collider.transform.parent.GetComponent<PlayerAttack>();
if(!playerAttack.target){
playerAttack.target = student;
}
}else if(collider.transform.parent.CompareTag(enemyTags[0]) || collider.transform.parent.CompareTag(enemyTags[1])){
StudentAttack studentAttack;
studentAttack = collider.GetComponent<StudentAttack>();
studentAttack.target = student;
}
}
}
void Block (string AT) {
block = false;
if(Random.Range(0, blockChance) == blockChance){
animation.Play("Block "+ AT);
block = true;
Debug.Log(student.name + " is blocking!");
}
}
void ApplyDamage (int damage) {
if(block) return;
if(health > 1){
health -= damage;
}else{
StartCoroutine(Die("Die"));
}
}
IEnumerator Die (string deathAnimation) {
squadInfo.members.Remove(student);
squadInfo.squad.Remove(student);
StudentAttack attack = student.GetComponent<StudentAttack>();
if(attack){
foreach(Transform enemy in attack.enemies){
if(!enemy.CompareTag("Player")){
StudentAttack attackE = enemy.GetComponent<StudentAttack>();
attackE.enemies.Remove(student);
if(attackE.target == student){
attackE.target = null;
}
}else{
PlayerAttack attackE = enemy.GetComponent<PlayerAttack>();
PlayerMovement moveE = enemy.GetComponent<PlayerMovement>();
if(attackE.target == student){
attackE.target = null;
}
if(moveE.allPlayers.Contains(student.gameObject)){
moveE.allPlayers.Remove(student.gameObject);
}
}
}
}
foreach(Transform member in squadInfo.members){
if(!member.CompareTag("Player")){
StudentController control = member.GetComponent<StudentController>();
control.friends.Remove(student);
if(control.leader == student){
control.leader = null;
}
}else{
if(squadInfo.currentLeader == member){
int d = 0;
int count = 0;
while(d < squadInfo.squad.Count){
if(squadInfo.squad[d] == student){
count = d;
}
d++;
}
member.parent.Find("Player HUD").SendMessage("RemoveFriend", count);
}
}
}
Destroy(student.GetComponent<StudentAttack>());
Destroy(student.GetComponent<StudentController>());
animation.Play(deathAnimation);
yield return new WaitForSeconds(animation[deathAnimation].length);
renderer.enabled = false;
collider.enabled = false;
int i = 0;
int a = 0;
if(foodPacks != 0){
while(i < foodPacks){
Vector3 randomLocation = student.position;
randomLocation.x += (Random.Range(-maxSpawnDistance, maxSpawnDistance));
randomLocation.z += (Random.Range(-maxSpawnDistance, maxSpawnDistance));
Instantiate(foodPack, randomLocation, student.rotation);
i++;
}
}
if(healthPacks != 0){
while(a < healthPacks){
Vector3 randomLocation = student.position;
randomLocation.x += (Random.Range(-maxSpawnDistance, maxSpawnDistance));
randomLocation.z += (Random.Range(-maxSpawnDistance, maxSpawnDistance));
Instantiate(healthPack, randomLocation, student.rotation);
a++;
}
}
Destroy(gameObject);
}
Attack
public bool canAttack = true;
public bool dueling = false;
public List<Transform> enemies = new List<Transform>();
public GameObject hD;
public float attackRadius = 1.0F;
public float attackDelay = 3;
public float minPunchDamage = 3;
public float maxPunchDamage = 7;
private float punchDamage = 5;
public float minKickDamage = 7;
public float maxKickDamage = 15;
private float kickDamage = 10;
public bool ranged;
public GameObject shuriken;
public GameObject knife;
public int shurikenDamage = 10;
public float shurikenSpeed = 10;
public int knifeDamage = 10;
public float knifeSpeed = 10;
private RaycastHit hit;
private Transform student;
public Transform target;
private bool attacking;
private Squad squadInfo;
private StudentController controller;
void Awake () {
student = transform;
}
void Start () {
squadInfo = student.parent.GetComponent<Squad>();
controller = student.GetComponent<StudentController>();
}
void Update () {
if(!canAttack) return;
if(!target){
target = controller.target;
}
if(!dueling){
foreach(Transform enemy in enemies){
if(!enemy.CompareTag("Player")){
StudentAttack attack = enemy.GetComponent<StudentAttack>();
if(attack.target == student){
controller.target = enemy;
target = enemy;
dueling = true;
}
}else{
PlayerAttack attack = enemy.GetComponent<PlayerAttack>();
if(attack.target == student){
controller.target = enemy;
target = enemy;
dueling = true;
}
}
}
}
if(target){
Physics.Raycast(student.position, student.forward, out hit, attackRadius);
if(!target.CompareTag("Player")){
StudentStats studentStats = target.GetComponent<StudentStats>();
StudentAttack studentAttack = target.GetComponent<StudentAttack>();
if(studentAttack){
if(studentAttack.target == student){
dueling = true;
}else{
dueling = false;
}
}
if(studentStats.health < 1){
target.SendMessage("Die", "Die");
StartCoroutine(SearchForTarget());
}
}else{
PlayerAttack playerAttack = target.GetComponent<PlayerAttack>();
if(playerAttack.target == student){
dueling = true;
}else{
dueling = false;
}
}
if(hit.transform == target){
if(!attacking){
punchDamage = Random.Range(minPunchDamage, maxPunchDamage);
kickDamage = Random.Range(minKickDamage, maxKickDamage);
punchDamage = Mathf.RoundToInt(punchDamage);
kickDamage = Mathf.RoundToInt(kickDamage);
if(Random.value > 0.5F){
StartCoroutine(MeleeAttack(punchDamage, "Punch"));
}else{
StartCoroutine(MeleeAttack(kickDamage, "Kick"));
}
}
}
}
}
void AttackStatus (bool change) {
canAttack = change;
}
IEnumerator SearchForTarget () {
yield return new WaitForEndOfFrame();
foreach(Transform member in squadInfo.members){
if(!member.CompareTag("Player")){
StudentAttack attack = member.GetComponent<StudentAttack>();
attack.enemies.Remove(target);
if(attack.target == target){
attack.target = null;
}
}
}
enemies.Remove(target);
target = null;
dueling = false;
}
IEnumerator MeleeAttack (float damage, string attackType) {
attacking = true;
animation.Play(attackType);
student.SendMessage("AttackType", attackType);
if(target !target.CompareTag("Player")){
target.SendMessage("Block", attackType);
}
yield return new WaitForSeconds(animation[attackType].length);
if(target){
bool block = false;
if(!target.CompareTag("Player")){
block = target.GetComponent<StudentStats>().block;
}else{
block = target.GetComponent<PlayerStats>().block;
}
if(!block){
target.SendMessage("ApplyDamage", damage);
GameObject ds = Instantiate(hD, target.position, Camera.main.transform.rotation) as GameObject;
ds.SendMessage("Reset", damage);
}
}
attacking = false;
}
Yes, the code is a bit long so you can just skim over it unless you want to go in depth into it. Thanks for your help!