diff options
author | chai <215380520@qq.com> | 2024-05-23 10:08:29 +0800 |
---|---|---|
committer | chai <215380520@qq.com> | 2024-05-23 10:08:29 +0800 |
commit | 8722a9920c1f6119bf6e769cba270e63097f8e25 (patch) | |
tree | 2eaf9865de7fb1404546de4a4296553d8f68cc3b /Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/ExampleScenes/Scenes/OldExamples/Example18_RTS/RTSUnit.cs | |
parent | 3ba4020b69e5971bb0df7ee08b31d10ea4d01937 (diff) |
+ astar project
Diffstat (limited to 'Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/ExampleScenes/Scenes/OldExamples/Example18_RTS/RTSUnit.cs')
-rw-r--r-- | Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/ExampleScenes/Scenes/OldExamples/Example18_RTS/RTSUnit.cs | 278 |
1 files changed, 278 insertions, 0 deletions
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/ExampleScenes/Scenes/OldExamples/Example18_RTS/RTSUnit.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/ExampleScenes/Scenes/OldExamples/Example18_RTS/RTSUnit.cs new file mode 100644 index 0000000..b1e0b9c --- /dev/null +++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/ExampleScenes/Scenes/OldExamples/Example18_RTS/RTSUnit.cs @@ -0,0 +1,278 @@ +using UnityEngine; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using UnityEngine.Profiling; + +namespace Pathfinding.Examples.RTS { + using Pathfinding; + using Pathfinding.RVO; + using Pathfinding.Util; + + [HelpURL("https://arongranberg.com/astar/documentation/stable/rtsunit.html")] + public class RTSUnit : VersionedMonoBehaviour { + public GameObject selectionIndicator; + public GameObject deathEffect; + public int team; + public float detectionRange; + + public float maxHealth; + + public enum Type { + Infantry, + Heavy, + Worker, + Harvester, + + HarvesterDropoff = 100, + HarvesterDropoffQueue, + ResourceCrystal = 200, + } + + public Type type; + [System.NonSerialized] + public float health; + IAstarAI ai; + RVOController rvo; + MovementMode movementMode; + Vector3 lastDestination; + RTSUnit attackTarget; + RTSWeapon weapon; + float lastSeenAttackTarget = float.NegativeInfinity; + bool reachedDestination; + public int storedCrystals; + public RTSUnit reservedBy; + public bool locked; + new Transform transform; + + /// <summary>Position at the start of the current frame</summary> + protected Vector3 position; + + public System.Action<bool> onMakeActiveUnit; + + public RTSPlayer owner { + get { + return RTSManager.instance.GetPlayer(team); + } + } + + public bool selectionIndicatorEnabled { + get { + if (selectionIndicator == null) return false; + return selectionIndicator.activeSelf; + } + set { + if (selectionIndicator != null) selectionIndicator.SetActive(value); + } + } + + public RTSHarvestableResource resource { + get { + return GetComponent<RTSHarvestableResource>(); + } + } + + public float radius { + get { + return rvo != null ? rvo.radius : 1f; + } + } + + bool mSelected; + public bool selected { + get { + return mSelected; + } + set { + mSelected = value; + selectionIndicatorEnabled = value; + if (value) { + RTSManager.instance.units.OnSelected(this); + } else { + RTSManager.instance.units.OnDeselected(this); + } + } + } + + public void OnMakeActiveUnit (bool active) { + if (onMakeActiveUnit != null) onMakeActiveUnit(active); + } + + public void SetDestination (Vector3 destination, MovementMode mode) { + if (ai != null && this) { + reachedDestination = false; + movementMode = mode; + ai.destination = lastDestination = destination; + (ai as AIBase).rvoDensityBehavior.ClearDestinationReached(); + ai.SearchPath(); + if (mode == MovementMode.Move) { + attackTarget = null; + } + } + } + + protected override void Awake () { + base.Awake(); + transform = (this as MonoBehaviour).transform; + ai = GetComponent<IAstarAI>(); + rvo = GetComponent<RVOController>(); + weapon = GetComponent<RTSWeapon>(); + } + + static System.Action<RTSUnit[], int> OnUpdateDelegate; + void OnEnable () { + if (OnUpdateDelegate == null) OnUpdateDelegate = OnUpdate; + RTSManager.instance.units.AddUnit(this); + selected = false; + health = maxHealth; + movementMode = MovementMode.AttackMove; + reachedDestination = true; + if (ai != null) lastDestination = ai.destination; + BatchedEvents.Add(this, BatchedEvents.Event.Update, OnUpdateDelegate); + } + + void OnDisable () { + BatchedEvents.Remove(this); + if (RTSManager.instance != null) RTSManager.instance.units.RemoveUnit(this); + } + + static void OnUpdate (RTSUnit[] units, int count) { + // Get some lists and arrays from an object pool + List<RTSUnit>[] unitsByOwner = ArrayPool<List<RTSUnit> >.ClaimWithExactLength(RTSManager.instance.PlayerCount); + for (int i = 0; i < unitsByOwner.Length; i++) { + unitsByOwner[i] = ListPool<RTSUnit>.Claim(); + } + for (int i = 0; i < count; i++) { + units[i].position = units[i].transform.position; + unitsByOwner[units[i].owner.index].Add(units[i]); + } + for (int i = 0; i < count; i++) { + units[i].OnUpdate(unitsByOwner); + } + + // Release allocated lists back to a pool + for (int i = 0; i < unitsByOwner.Length; i++) { + ListPool<RTSUnit>.Release(ref unitsByOwner[i]); + } + ArrayPool<List<RTSUnit> >.Release(ref unitsByOwner, true); + } + + // Update is called once per frame + protected virtual void OnUpdate (List<RTSUnit>[] unitsByOwner) { + if (ai == null) { + // Stationary unit + + if (weapon != null) { + float minDist = detectionRange*detectionRange; + for (int player = 0; player < unitsByOwner.Length; player++) { + if (!owner.IsHostile(RTSManager.instance.GetPlayer(player))) continue; + + for (int i = 0; i < unitsByOwner[player].Count; i++) { + var unit = unitsByOwner[player][i]; + var dist = (unit.position - position).sqrMagnitude; + if (dist < minDist) { + attackTarget = unit; + minDist = dist; + } + } + } + + if (attackTarget != null) { + if (!weapon.InRangeOf(attackTarget.position)) { + attackTarget = null; + } else { + if (weapon.Aim(attackTarget)) { + weapon.Attack(attackTarget); + } + } + } + } + + if (attackTarget != null) { + lastSeenAttackTarget = Time.time; + } + } else { + rvo.locked = false | locked; + + // this.reachedDestination will be true once the AI has reached its destination + // and it will stay true until the next time SetDestination is called. + reachedDestination |= (ai as AIBase).rvoDensityBehavior.reachedDestination; + + if (weapon != null) { + bool canAttack = movementMode == MovementMode.AttackMove; + // This takes into account path calculations as well as if the AI stops far away from the destination due to being part of a large group + canAttack |= reachedDestination && movementMode == MovementMode.Move; + + if (canAttack) { + float minDist = detectionRange*detectionRange; + + Profiler.BeginSample("Distance"); + var pos = position; + for (int player = 0; player < unitsByOwner.Length; player++) { + if (!owner.IsHostile(RTSManager.instance.GetPlayer(player))) continue; + + var enemies = unitsByOwner[player]; + for (int i = 0; i < enemies.Count; i++) { + var enemy = enemies[i]; + var dist = (enemy.position - pos).sqrMagnitude; + if (dist < minDist) { + attackTarget = enemy; + minDist = dist; + } + } + } + Profiler.EndSample(); + + float rangeFuzz = 1.1f; + if (attackTarget != null && (attackTarget.position - position).magnitude > detectionRange*rangeFuzz) { + attackTarget = null; + } + + bool wantsToAttack = false; + + if (attackTarget != null) { + if (!weapon.InRangeOf(attackTarget.position)) { + ai.destination = attackTarget.position; + } else { + wantsToAttack = true; + if (weapon.Aim(attackTarget)) { + weapon.Attack(attackTarget); + } + } + } + + if (attackTarget != null) { + lastSeenAttackTarget = Time.time; + } + + if (!weapon.canMoveWhileAttacking && (wantsToAttack || weapon.isAttacking)) { + rvo.locked = true; + } + } + } + + // Move back to original destination in case we followed an enemy for some time + if (Time.time - lastSeenAttackTarget > 2) { + ai.destination = lastDestination; + } + } + } + + public void Die () { + StartCoroutine(DieCoroutine()); + } + + IEnumerator DieCoroutine () { + yield return new WaitForEndOfFrame(); + if (deathEffect != null) GameObject.Instantiate(deathEffect, transform.position, transform.rotation); + GameObject.Destroy(gameObject); + } + + public void ApplyDamage (float damage) { + health = Mathf.Clamp(health - damage, 0, maxHealth); + if (health <= 0) { + Die(); + } + } + } +} |