Skip to content

Instantly share code, notes, and snippets.

@evanlemmons
Created May 4, 2021 21:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save evanlemmons/0a6db4aa5fccb74188732116420c1af3 to your computer and use it in GitHub Desktop.
Save evanlemmons/0a6db4aa5fccb74188732116420c1af3 to your computer and use it in GitHub Desktop.
Code related to targeting and pathfinding logic
//UnitCore.cs
//removed some unrelated
private void OnEnable()
{
UnitTracker.Instance.ActivateUnit(this, allegiance);
orders = allegiance == Allegiance.Player ? BattleOrderManager.Instance.playerOrders : BattleOrderManager.Instance.enemyOrders;
RefreshOrders();
}
public void RefreshOrders()
{
NewOrders(orders);
}
public void NewOrders(BattleOrders newOrders)
{
orders = newOrders;
if (!meleeAttack.isAttacking && !statusEffects.isImpaired)
{
switch (orders)
{
case BattleOrders.Advance:
targeting.SeekNewOpening();
break;
case BattleOrders.Idle:
targeting.ClearTargets();
animationController.StateController(UnitAnimation.Idling);
break;
default:
break;
}
}
}
//removed some unrelated code
public class UnitOpening : MonoBehaviour
{
private UnitCore _core;
private Allegiance _allegiance;
public UnitOpenings myController;
public UnitCore occupant;
public List<UnitCore> incomingUnits;
private void Awake()
{
_core = DFUtilities.GetUnitCore(gameObject);
_allegiance = _core.allegiance;
}
private void OnEnable()
{
UnitTracker.Instance.ActivateOpening(this, _allegiance);
}
//-- INCOMING UNITS --//
public void Targeted(UnitCore unit)
{
//TODO: Be smarter about multiple units targeting the same opening.
//Find a way to determine closest via pathing and redirect the furthest one
incomingUnits.Add(unit);
}
private void RedirectIncomingUnits()
{
if (incomingUnits.Count > 0)
{
foreach (UnitCore unit in incomingUnits)
{
if (occupant && unit != occupant)
unit.RefreshOrders();
else if (!occupant)
unit.RefreshOrders();
}
incomingUnits.Clear();
}
}
//-- AN ENEMY OCCUPIES THIS OPENING --//
private void OnTriggerStay(Collider other)
{
if (!occupant)
{
if (other.CompareTag("Troop") || other.CompareTag("General"))
if (other.GetComponent<UnitCore>().allegiance != _allegiance)
{
UnitTracker.Instance.DeactivateOpening(this, _allegiance);
occupant = other.GetComponent<UnitCore>();
occupant.OnDefeated += OccupantRemovedOrDefeated;
myController.UpdateOpeningVacancy(this);
RedirectIncomingUnits();
}
}
else
if (other.CompareTag("Troop") || other.CompareTag("General"))
if (other.GetComponent<UnitCore>().allegiance != _allegiance && other.GetComponent<UnitCore>() != occupant)
other.GetComponent<UnitCore>().RefreshOrders();
}
//-- OCCUPANT MOVES OUT OF THIS OPENING --//
private void OnTriggerExit(Collider other)
{
if (occupant && other.GetComponent<UnitCore>() == occupant)
{
OccupantRemovedOrDefeated();
}
}
//-- REACTIVATE THIS OPENING --//
private void OccupantRemovedOrDefeated()
{
occupant.OnDefeated -= OccupantRemovedOrDefeated;
occupant = null;
UnitTracker.Instance.ActivateOpening(this, _allegiance);
myController.UpdateOpeningVacancy(this);
}
}
//removed some unrelated code
public class UnitPathing : MonoBehaviour
{
private UnitCore _core;
public RichAI richAI;
public AIDestinationSetter destinationSetter;
public Seeker seeker;
private void Awake()
{
_core = DFUtilities.GetUnitCore(gameObject);
}
private void OnEnable()
{
_core.animationController.OnBeginAttackAnimation += DisableMovement;
_core.animationController.OnStateChanged += UpdateCanMove;
}
public UnitOpening GetClosestOpening(List<UnitOpening> opponentOpenings)
{
Vector3[] endPoints = new Vector3[opponentOpenings.Count];
for (int i = 0; i < endPoints.Length; i++)
{
endPoints[i] = opponentOpenings[i].transform.position;
}
var path = MultiTargetPath.Construct(transform.position, endPoints, null);
AstarPath.StartPath(path);
path.BlockUntilCalculated();
var closestOpeningIndex = path.chosenTarget;
return opponentOpenings[closestOpeningIndex];
}
public UnitCore GetClosestUnit(List<UnitCore> opponentUnits)
{
Vector3[] endPoints = new Vector3[opponentUnits.Count];
for (int i = 0; i < endPoints.Length; i++)
{
endPoints[i] = opponentUnits[i].transform.position;
}
//TODO: This isn't actually calcualting the closest path. Even without using terrain, on a completely flat plane this doesn't always select the nearest opening
var path = MultiTargetPath.Construct(transform.position, endPoints, null);
AstarPath.StartPath(path);
path.BlockUntilCalculated();
var closestOpeningIndex = path.chosenTarget;
return opponentUnits[closestOpeningIndex];
}
private void UpdateCanMove(UnitAnimation animation)
{
switch (animation)
{
case UnitAnimation.Attacking:
richAI.canMove = false;
richAI.canSearch = false;
break;
case UnitAnimation.Idling:
richAI.canMove = false;
richAI.canSearch = true;
break;
case UnitAnimation.Moving:
richAI.canMove = true;
richAI.canSearch = true;
break;
case UnitAnimation.Impaired:
richAI.canMove = true;
richAI.canSearch = false;
break;
default:
Debug.Log(_core.name + " UnitPathing UpdateCanMove received unknown UnitAnimation: " + animation);
richAI.canMove = false;
richAI.canSearch = false;
break;
}
}
private void DisableMovement()
{
richAI.canMove = false;
}
}
//removed some unrelated code
public class UnitTargeting : MonoBehaviour
{
public UnitCore targetedUnit;
public UnitOpening targetedOpening;
private UnitCore _core;
private List<UnitOpening> _vacantOpponentOpenings;
private List<UnitCore> _activeOpponentUnits;
private void Awake()
{
_core = DFUtilities.GetUnitCore(gameObject);
_vacantOpponentOpenings = _core.allegiance == Allegiance.Player ? UnitTracker.Instance.enemyOpenings : UnitTracker.Instance.playerOpenings;
_activeOpponentUnits = _core.allegiance == Allegiance.Player ? UnitTracker.Instance.playerUnits : UnitTracker.Instance.playerUnits;
}
//-- SCAN FOR OPPONENTS --//
public void SeekNewOpening()
{
//TODO: Implement swarming
//TODO: Help units make better targeting decisions by considering the distance of other units who have the same opening targeted.
if (_core.statusEffects.isImpaired || !FindObjectOfType<AstarPath>())
return;
if (_vacantOpponentOpenings.Count > 0)
{
UnitTracker.Instance.OnOpeningVacancy -= NewOpeningAvailable;
//Find the closest opening
targetedOpening = _core.pathing.GetClosestOpening(_vacantOpponentOpenings);
targetedUnit = targetedOpening.GetComponentInParent<UnitCore>();
//Tell the opening you're coming for it
targetedOpening.Targeted(_core);
//Start moving
_core.pathing.destinationSetter.target = targetedOpening.transform;
_core.animationController.StateController(UnitAnimation.Moving);
}
else if (_activeOpponentUnits.Count > 0)
{
//If no openings are available, listen for new vacancies
UnitTracker.Instance.OnOpeningVacancy += NewOpeningAvailable;
//Instead, find the closest unit
targetedOpening = null;
targetedUnit = _core.pathing.GetClosestUnit(_activeOpponentUnits);
//TODO: Swarm to random points around this unit
//Until I get that figured out, they'll just stand idle
ClearTargets();
_core.animationController.StateController(UnitAnimation.Idling);
}
else
{
UnitTracker.Instance.OnOpeningVacancy -= NewOpeningAvailable;
ClearTargets();
_core.animationController.StateController(UnitAnimation.Idling);
}
}
private void NewOpeningAvailable(Allegiance a)
{
//If you're melee attacking, it will call core.RefreshOrders() when done so you don't need to listen for new openings anymore
if (_core.meleeAttack.isAttacking)
{
UnitTracker.Instance.OnOpeningVacancy -= NewOpeningAvailable;
return;
}
//If you're not attacking it means all enemies are defeated or you're awaiting a new opening
if(_activeOpponentUnits.Count > 0 && a != _core.allegiance && _core.orders == BattleOrders.Advance)
SeekNewOpening();
}
//-- IDLING OR IMPAIRED --//
public void ClearTargets()
{
targetedOpening = null;
targetedUnit = null;
_core.pathing.destinationSetter.target = null;
}
//-- OPPONENTS ENTER MELEE RANGE --//
public void OpponentInMeleeRange()
{
targetedOpening = null;
targetedUnit = _core.openingController.occupiedOpenings[0].occupant;
if (targetedUnit.gameObject.activeInHierarchy)
_core.meleeAttack.Initiate();
else
{
targetedUnit = null;
_core.RefreshOrders();
}
}
}
public class UnitTracker : MonoSingleton<UnitTracker>
{
public UnitCore playerGeneral, enemyGeneral;
public List<UnitCore> playerUnits, enemyUnits;
public List<UnitOpening> playerOpenings, enemyOpenings;
public Action<Allegiance> OnOpeningVacancy;
public Action<UnitCore, Allegiance> OnUnitAdded, OnUnitRemoved;
public Action OnAllPlayersDefeated, OnAllEnemiesDefeated;
public void ActivateUnit(UnitCore unit, Allegiance allegiance)
{
switch(allegiance)
{
case Allegiance.Player:
if (!playerUnits.Contains(unit))
playerUnits.Add(unit);
if (unit.CompareTag("General"))
playerGeneral = unit;
break;
case Allegiance.Enemy:
if (!enemyUnits.Contains(unit))
enemyUnits.Add(unit);
if (unit.CompareTag("General"))
enemyGeneral = unit;
break;
default:
Debug.LogError(unit + " is missing an Allegiance setting.");
break;
}
OnUnitAdded?.Invoke(unit, allegiance);
}
public void DeactivateUnit(UnitCore unit, Allegiance allegiance)
{
switch (allegiance)
{
case Allegiance.Player:
if(playerUnits.Contains(unit))
playerUnits.Remove(unit);
break;
case Allegiance.Enemy:
if (enemyUnits.Contains(unit))
enemyUnits.Remove(unit);
break;
default:
Debug.LogError(unit + " is missing an Allegiance setting.");
break;
}
OnUnitRemoved?.Invoke(unit, allegiance);
if (playerUnits.Count == 0)
OnAllPlayersDefeated?.Invoke();
if (enemyUnits.Count == 0)
OnAllEnemiesDefeated?.Invoke();
}
public void ActivateOpening(UnitOpening opening, Allegiance allegiance)
{
switch (allegiance)
{
case Allegiance.Player:
if(!playerOpenings.Contains(opening))
playerOpenings.Add(opening);
break;
case Allegiance.Enemy:
if (!enemyOpenings.Contains(opening))
enemyOpenings.Add(opening);
break;
default:
Debug.LogError(opening + " is missing an Allegiance setting.");
break;
}
OnOpeningVacancy?.Invoke(allegiance);
}
public void DeactivateOpening(UnitOpening opening, Allegiance allegiance)
{
switch (allegiance)
{
case Allegiance.Player:
if(playerOpenings.Contains(opening))
playerOpenings.Remove(opening);
break;
case Allegiance.Enemy:
if (enemyOpenings.Contains(opening))
enemyOpenings.Remove(opening);
break;
default:
Debug.LogError(opening.gameObject + " is missing an Allegiance setting.");
break;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment