Created
May 4, 2021 21:17
-
-
Save evanlemmons/0a6db4aa5fccb74188732116420c1af3 to your computer and use it in GitHub Desktop.
Code related to targeting and pathfinding logic
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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(); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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