I’m working on a moba that is a work in progress of course, but I’m having way too much issue with the targeting system. I’m more about the logic of it I guess. I can pretty much put anything together with help from documentation and a little thinking. But, I’ve come up on a brick wall for this one.
My minions are supposed to only have five things that they can target. A player, waypoints, another minion, a neutral monster (P v E), and towers of some sort(tower, keeps, heals, core). I will post the code here so I’m not just trying to explain air to you.
using UnityEngine;
using UnityEngine.UI;
using System.Collections;
public class CreepEnemy : MonoBehaviour
{
#region Global variable Declaration
public float timeTillAttack = 5f;
public float minDamage = 5f;
public float maxDamage = 10f;
public float acceptableSDistance = 5f;
public float canAgroEnemy = 50f;
public float outLimitsOfDetection = 1500f;
public float distanceFromEnemy = 5f;
public float movementSpeed = 4f;
public float turnSpeed = 3f;
public float distToEnemy;
public float distToTower;
public Transform target = null;
public Transform wayPoint;
public int wayPointIndex = 0;
public Transform targetEnemy;
public Transform tower;
Transform myTransform;
float attackTimer = 5f;
Animator anim;
bool running = false;
bool canAttack = false;
bool goForTower = false;
float targetingTimer = 2.5f;
float targetingTimerLimit = 2.5f;
float closeEnoughToWP = 0.5f;
#endregion
void Awake()
{
myTransform = transform;
anim = GetComponent<Animator>();
}
void Start()
{
GameMasterObject.enemyCreeps.Add(myTransform);
wayPointIndex = WayPointManager.points.Length - 1;
}
void Update()
{
MoveToTarget();
//Debug.Log(WayPointManager.points.Length - 1);
#region set attack state
if (attackTimer > 0)
{
attackTimer -= Time.deltaTime;
}
else if (attackTimer <= 0 && canAttack)
{
if (anim != null)
{
anim.SetTrigger("Attack");
}
attackTimer = timeTillAttack;
}
#endregion
FindWayPoints:
#region move towards a target
#region if targetEnemy is not null
if (targetEnemy != null)
{
try
{
distToEnemy = Vector3.Distance(targetEnemy.position, myTransform.position);
if (distToEnemy > acceptableSDistance && distToEnemy > canAgroEnemy && distToEnemy < outLimitsOfDetection)
{
RotateToTarget(wayPoint);
canAttack = false;
#region check for Tower
if (tower == null)
{
try
{
RotateToTarget(wayPoint);
}
catch (System.Exception e)
{
Debug.Log(e);
}
}
if (tower != null)
{
try
{
RotateToTarget(tower);
distToTower = Vector3.Distance(tower.position, myTransform.position);
if (distToTower <= acceptableSDistance)
{
canAttack = true;
}
else
{
canAttack = false;
}
}
catch (System.Exception e)
{
Debug.Log(e);
}
}
#endregion
}
else if (distToEnemy > acceptableSDistance && distToEnemy <= canAgroEnemy)
{
RotateToTarget(targetEnemy);
canAttack = false;
//Debug.Log("canAgro");
}
else if (distToEnemy <= acceptableSDistance)
{
//Debug.Log("canAttack");
canAttack = true;
}
else if (distToEnemy > outLimitsOfDetection)
{
canAttack = false;
targetEnemy = null;
}
}
catch (System.Exception e)
{
goto FindWayPoints;
}
}
#endregion
#region if targetEnemy is null
else if (targetEnemy == null)
{
if (tower == null)
{
try
{
RotateToTarget(wayPoint);
}
catch (System.Exception e)
{
Debug.Log(e);
}
}
if (tower != null)
{
try
{
RotateToTarget(tower);
distToTower = Vector3.Distance(tower.position, myTransform.position);
if (distToTower <= acceptableSDistance)
{
canAttack = true;
}
else
{
canAttack = false;
}
}
catch (System.Exception e)
{
Debug.Log(e);
}
}
if (targetingTimer <= 0)
{
targetEnemy = FindTargetEnemy();
targetingTimer = targetingTimerLimit;
}
else if (targetingTimer > 0)
{
targetingTimer -= Time.deltaTime;
}
}
#endregion
#region set states
if (target != null)
{
if (!canAttack)
{
running = true;
}
else if (canAttack)
{
running = false;
}
}
else if (target == null)
{
canAttack = false;
running = false;
}
#endregion
#endregion
}
void Attack()
{
if (target == null)
return;
if (tower == null)
{
IDamageable enemy = target.GetComponent<IDamageable>();
if (enemy != null)
{
enemy.TakeDamage(Random.Range(minDamage, maxDamage), target.position);
}
}
}
void RotateToTarget(Transform _target)
{
target = _target;
Vector3 dir = target.position - myTransform.position;
Quaternion lookDirection = Quaternion.LookRotation(dir);
Quaternion facingDir = Quaternion.Slerp(myTransform.rotation, lookDirection, Time.deltaTime * turnSpeed);
Quaternion faceNow = Quaternion.Euler(0f, facingDir.eulerAngles.y, 0f);
myTransform.rotation = faceNow;
}
void MoveToTarget()
{
if (anim != null)
{
anim.SetBool("Running", running);
}
if (running)
{
myTransform.Translate(Vector3.forward * Time.deltaTime * movementSpeed);
}
}
Transform FindTargetEnemy()
{
//Debug.Log("Checking for enemies");
Transform tempTarget = null;
float lastEnemyDist = Mathf.Infinity;
#region if shark lions are here
if (GameMasterObject.dragonsMonsters.Count > 0)
{
for (int j = 0; j < GameMasterObject.enemyCreeps.Count; j++)
{
float dist = Vector3.Distance(myTransform.position, GameMasterObject.dragonsMonsters[j].position);
if (dist < lastEnemyDist && dist < outLimitsOfDetection && GameMasterObject.dragonsMonsters[j].gameObject.activeInHierarchy)
{
tempTarget = GameMasterObject.dragonsMonsters[j];
lastEnemyDist = dist;
}
}
acceptableSDistance = 9f;
}
#endregion
else if (GameMasterObject.dragonsMonsters.Count <= 0)
{
for (int i = 0; i < GameMasterObject.heroCreeps.Count; i++)
{
tempTarget = GameMasterObject.heroCreeps[Random.Range(0, GameMasterObject.enemyCreeps.Count - 1)];
}
}
return tempTarget;
}
void OnTriggerEnter(Collider other)
{
if (other.gameObject.tag == "WayPoint")
{
wayPoint = other.gameObject.GetComponent<WayPoint>().nextEnemyWayPoint;
}
if (other.gameObject.tag == "Tower")
{
goForTower = true;
tower = other.gameObject.GetComponentInChildren<TowerHealth>().transform;
acceptableSDistance = other.gameObject.GetComponentInChildren<TowerHealth>().distanceFromMe;
}
if (other.gameObject.tag == "Ally")
{
targetEnemy = other.gameObject.transform;
acceptableSDistance = distanceFromEnemy;
}
}
void OnCollisionEnter(Collision collision)
{
if (collision.gameObject.tag == "Ally")
{
Debug.Log("Ally");
targetEnemy = collision.gameObject.transform;
acceptableSDistance = distanceFromEnemy;
}
}
void OncollsionStay(Collision collision)
{
Debug.Log("Ally");
targetEnemy = collision.gameObject.transform;
acceptableSDistance = distanceFromEnemy;
}
}
ok. So that’s all of my minion code.
So what happens is, they are supposed to choose a random enemy from the list of enemies in my GameMasterObject script. If there is an enemy(another minion), then that is the priority. No one else should come first. Then once that’s done, they are supposed to choose another random. They do that just fine. The problem is, they will bump into each other trying to pass and get to another enemy. Well, I set an OnCollisionEnter and Stay for that so that they grab the minion that they just bumped into as their new target enemy. But it doesn’t work. Well at least not like I want it to. It’s not consistent. And then sometimes they stop and act as if they are attacking something in front of them but they are actually targeting something across the field. The on collision stay should have fixed that.
Now, I know if I change to trigger, that would fix the problem but I kinda wanted to keep the real colliders for more collsions instead of using trigger detection that let’s you pass through. Feel free to take what you want for your game. If implemented without the dynamic change based on collisions, the code works.