Enemy unit that is far moving before enemy unit that is closer

I reviewed the code and cannot see why this might be happening. Looks identical to Code Monkey’s code. Any clues? Thanks.

There’s no logic to the order in which enemies take their turns. The turns are determined by the order they are added to the list. Besides, what is ‘closer’? Closer to what? If you can answer that, you can order the list based on that information before taking the enemy turns.

For example, add a function to UnitManager to give you an ordered list

public List<Unit> GetEnemyUnitListOrdered<TKey>(Func<Unit, TKey> keySelector)
{
    return enemyUnitList.OrderBy(keySelector).ToList();
}

This uses Linq but it should be ok (see the end of the post for a non-Linq version)

You can now specify how you want the list to be ordered. For example, let the enemies closest to the camera attack first

// in EnemyAI.cs
private bool TryTakeEnemyAIAction(Action onEnemyAIActionComplete)
{
    // Get a list of enemy units ordered from closest to farthest from the camera
    List<Unit> orderedEnemyUnitList = UnitManager.Instance
        .GetEnemyUnitListOrdered(unit => CalculateSqrDistanceToCamera(unit.transform.position));
    foreach (Unit enemyUnit in orderedEnemyUnitList)
    {
        if (TryTakeEnemyAIAction(enemyUnit, onEnemyAIActionComplete))
        {
            return true;
        }
    }

    return false;
}

private float CalculateSqrDistanceToCamera(Vector3 target)
{
    return (Camera.main.transform.position - target).sqrMagnitude;
}

Camera is not a good example because you can position the camera behind the enemies, but it’s an example. You’d have to figure out what you mean by ‘closest’ and use that


If you really don’t want the Linq, here’s a different version. It’s not going to be much different because we’re creating a temporary list because the sort is in-place. You could probably get away with not creating a copy.

public List<Unit> GetEnemyUnitListOrdered<TKey>(Func<Unit, TKey> keySelector)
    where TKey : IComparable
{
    List<Unit> sorted = new List<Unit>(enemyUnitList);
    sorted.Sort((unitA, unitB) => keySelector(unitA).CompareTo(keySelector(unitB)));
    return sorted;
}
2 Likes

Great answer. Thank you @bixarrio

I’m not sure this is 100% correct, though. I looked at the code again and this ordering happens a couple of times during the enemies’ turn. If the camera moves (for example) in their turn, the order of the list will change. Maybe it’s not such a big deal. An enemy unit will only attack if it can. If the order changed and the enemy is checked again, it will just say ‘no’ and we’ll move to the next one.

It may even be a good thing. If you were to calculate the perceived center of mass of the player units and use that as the ‘closer’ target, that perceived center of mass will change if a player unit is killed and the enemies will adjust to accommodate that change.

Here it is (I think)

private bool TryTakeEnemyAIAction(Action onEnemyAIActionComplete)
{
    // Get a list of enemy units ordered from closest to farthest from the center of all player units
    List<Unit> orderedEnemyUnitList = UnitManager.Instance
        .GetEnemyUnitListOrdered(unit => CalculateSqrDistanceToPlayerUnits(unit.transform.position));
    foreach (Unit enemyUnit in orderedEnemyUnitList)
    {
        if (TryTakeEnemyAIAction(enemyUnit, onEnemyAIActionComplete))
        {
            return true;
        }
    }

    return false;
}

private float CalculateSqrDistanceToPlayerUnits(Vector3 target)
{
    Vector3 centerOfMass = CalculatePerceivedCenterOfMass(UnitManager.Instance.GetFriendlyUnitList());
    return (centerOfMass - target).sqrMagnitude;
}
private Vector3 CalculatePerceivedCenterOfMass(List<Unit> units)
{
    Vector3 sumOfPositions = Vector3.zero;
    foreach(Unit unit in units)
    {
        sumOfPositions += unit.GetWorldPosition();
    }
    return sumOfPositions / units.Count;
}
2 Likes

Privacy & Terms