using UnityEngine; using System.Collections; using System.Collections.Generic; using Pathfinding; using System.Threading.Tasks; using System; using System.Linq; using System.Text; // Designed by the goat Jacob Weedman // Use on any basic enemy // // Configure to your likeing // Not all options apply with every configuration // // PREREQUISITES // 1. The "Seeker" script from the A* pathfinding library must be attatched to the same game object // 2. A Rigidbody2D component must be attatched to the same game object // 3. A _Collider2D component must be attatched to the same game object // 4. Various game objects are required to be in the scene in order for this to function properly // 5. Various children game objects are required in order for this to funciton properly public class MasterEnemyAI : MonoBehaviour { #region MISC VARIABLES (DO NOT CHANGE VALUES) GameObject Target = null; GameObject Camera = null; GameObject Player = null; GameObject Barrel = null; Vector2 StartPosition; public GameObject LastKnownPlayerLocation = null; float DistanceFromPlayer; public List SuitablePositions; public List AllPositions; public List PatrolPositions; // start pathfinding float nextWaypointDistance = 5; Path path = null; int currentWaypoint = 0; bool reachedEndOfPath = false; // end pathfinding #endregion #region CONDITIONS/GENERAL INFORMATION bool PositionRecomputeLock = false; public bool TargetingPlayer = false; public bool CurrentlyReloading = false; bool PatrolLock = false; bool Lock = false; bool MoveLock = false; bool FireLock = false; bool DashLock = false; bool TeleportLock = true; bool CurrentlyMovingToNextPatrolTarget = false; float DesiredDistance; public int WeaponCurrentMagazineAmmount; public int NumberOfHitsPerMag = 0; float BarrelAngle; bool Primed = false; // For self-explosion #endregion #region ENEMY STATS (Changeable in Unity) public float Speed = 1f; // In relation to player's walking speed public float JumpHeight = 1.5f; // In relation to player's regular jump height public float Health = 100; public float MinumumDistance = 5f; public float PatrolDistance = 10; public int PlayerDetectionRange = 50; float PatrolCooldown; public float PatrolCooldownReset = 20.0f; float JumpCooldown; public float JumpCooldownReset = 1.0f; float FireCooldown; public float FireCooldownReset = 0.1f; float ReloadCooldown; public float ReloadCooldownReset = 5.0f; public float DashSpeed = 40f; // Strength of the dash float DashCooldown; public float DashCooldownReset = 4.0f; float TeleportCooldown; public float TeleportCooldownReset = 0.5f; public float HiddenTransparencyAmmount = 0.05f; // Strength of the cloaked transparency public int MaxBatteryCapacity = 30; // Depletes one every second it is out of the bay public int RechargeTime = 6000; //ms // Capabiltiies public bool AbilityDash = false; public bool AbilityInvisible = false; public bool AbilityJump = true; public bool AbilityTeleport = false; public bool AbilityReloadAndMove = false; public bool AbilityMove = true; public bool AbilityDynamicRelocation = true; public bool AbilityExplodeOnContact = false; public bool AbilityExplodeNearPlayer = false; public bool AbilityExplodeOnDeath = false; public bool AbilityPlayerESP = false; #endregion #region WEAPON STATS (CHANGEABLE in Unity) public GameObject EnemyProjectile; public string EnemyType = "GROUND"; // Options: "GROUND", "AIR" public string WeaponType = "RANGED"; // Options: "RANGED", "ROCKET", "PARTICLE", "MELEE", "GRENADE", "NONE" public int WeaponDamage = 5; // Damage per hit public float WeaponRandomSpread = 7.5f; // Random direction of lanched projectiles public int WeaponRange = 15; // Maximum range of the projectile before it drops off public float WeaponProjectileSpeed = 40f; // Speed of launched projectiles public int WeaponMagazineSize = 20; // Number of shots the enemy will take before having to reloade #endregion #region COMPONENT REFERENCES Seeker seeker = null; Rigidbody2D rb = null; #endregion #region ONCE THE ENEMY IS SPAWNED IN void Awake() { if (GetComponent()) seeker = GetComponent(); if (GetComponent()) rb = GetComponent(); if (transform.Find("ReloadingIndicator")) transform.Find("ReloadingIndicator").GetComponent().enabled = false; if (transform.Find("PursuingIndicator")) transform.Find("PursuingIndicator").GetComponent().enabled = false; StartPosition = transform.position; if (GameObject.FindGameObjectsWithTag("PossiblePositions").Count() > 0) AllPositions = GameObject.FindGameObjectsWithTag("PossiblePositions").ToList(); if (EnemyType == "GROUND") // Configure settings for Ground enemies { foreach (GameObject pos in AllPositions) { if (Vector2.Distance(pos.transform.position, StartPosition) <= PatrolDistance) { PatrolPositions.Add(pos); } } gameObject.layer = LayerMask.NameToLayer("Enemies"); foreach (Transform child in gameObject.transform) { child.gameObject.layer = LayerMask.NameToLayer("Enemies"); } if (PatrolPositions.Count() > 0) { Target = PatrolPositions[UnityEngine.Random.Range(0, PatrolPositions.Count)]; } else { Target = null; } } if (EnemyType == "AIR") // Configure settings for Air enemies { gameObject.layer = LayerMask.NameToLayer("FlyingEnemies"); foreach (Transform child in gameObject.transform) { child.gameObject.layer = LayerMask.NameToLayer("FlyingEnemies"); } rb.gravityScale = 0; if (GameObject.Find("FlyingTarget")) Target = Instantiate(GameObject.Find("FlyingTarget"), transform.position, Quaternion.identity); } if (GameObject.FindGameObjectWithTag("MainCamera")) Camera = GameObject.FindGameObjectWithTag("MainCamera"); if (GameObject.FindGameObjectWithTag("Player")) Player = GameObject.FindGameObjectWithTag("Player"); if (transform.Find("Barrel")) Barrel = transform.Find("Barrel").gameObject; WeaponCurrentMagazineAmmount = WeaponMagazineSize; DesiredDistance = WeaponRange - 5; if (EnemyProjectile == null) // Automatically set enemy projectile if level designer did not { switch (WeaponType) { case "RANGED": EnemyProjectile = GameObject.Find("GameObjectFolder").transform.Find("EnemyProjectile").gameObject; break; case "ROCKET": EnemyProjectile = GameObject.Find("GameObjectFolder").transform.Find("EnemyRocket").gameObject; break; case "PARTICLE": EnemyProjectile = GameObject.Find("GameObjectFolder").transform.Find("EvilAura").gameObject; break; case "GRENADE": EnemyProjectile = GameObject.Find("GameObjectFolder").transform.Find("Grenade").gameObject; break; default: EnemyProjectile = GameObject.Find("GameObjectFolder").transform.Find("EnemyProjectile").gameObject; break; } } InvokeRepeating("UpdatePath", 0f, 0.1f); InvokeRepeating("PathfindingTimeout", 0f, 30); // Initialize timers JumpCooldown = JumpCooldownReset; FireCooldown = FireCooldownReset; ReloadCooldown = ReloadCooldownReset; DashCooldown = DashCooldownReset; PatrolCooldown = 0; TeleportCooldown = TeleportCooldownReset; } #endregion #region INVOKE REPEATING METHODS // When enemy has reached the next node void OnPathComplete(Path p) { if (!p.error) { path = p; currentWaypoint = 0; } } // Select next node void UpdatePath() { if (seeker.IsDone() && Target != null) { seeker.StartPath(rb.position, Target.transform.position, OnPathComplete); } } // Pathfiniding Timeout void PathfindingTimeout() { if (Target != null && Vector2.Distance(transform.position, Target.transform.position) > 0.5 || LastKnownPlayerLocation == null) { LastKnownPlayerLocation = null; MoveNextPatrol(); if (AbilityTeleport && TeleportLock == false) { Teleport(); } } } #endregion #region MAIN LOGIC void FixedUpdate() { #region DEATH if (Health <= 0) { if (AbilityExplodeNearPlayer == false && AbilityExplodeOnContact == false && AbilityExplodeOnDeath == false) { // Create carcas GameObject DeadBody; DeadBody = Instantiate(gameObject, transform.position, Quaternion.identity); DeadBody.GetComponent().gravityScale = 1.5f; Destroy(GameObject.Find(DeadBody.name).GetComponent()); Destroy(GameObject.Find(DeadBody.name).GetComponent()); foreach (Transform child in DeadBody.transform) { GameObject.Destroy(child.gameObject); } Destroy(gameObject); } else if (AbilityExplodeOnDeath == true) { Explode(); } } // Explode when close to the player if (AbilityExplodeNearPlayer && Primed && Vector2.Distance(Player.transform.position, transform.position) <= 2) { Explode(); } #endregion #region ICONS // See where the enemy wants to go (DEBUGGING ONLY) /* if (Target != null) { foreach (GameObject pos in AllPositions) { pos.GetComponent().enabled = false; } Target.GetComponent().enabled = true; } */ if (TargetingPlayer && transform.Find("PursuingIndicator")) { transform.Find("PursuingIndicator").GetComponent().enabled = true; if (AbilityInvisible) { Cloak(false); } } else if (transform.Find("PursuingIndicator")) { transform.Find("PursuingIndicator").GetComponent().enabled = false; if (AbilityInvisible) { Cloak(true); } } if (CurrentlyReloading && transform.Find("ReloadingIndicator")) { transform.Find("ReloadingIndicator").GetComponent().enabled = true; } else if (transform.Find("ReloadingIndicator")) { transform.Find("ReloadingIndicator").GetComponent().enabled = false; } //if (90 <= angle || angle <= 270) // FIX THIS //{ // barrel.transform.localScale = new Vector2(-barrel.transform.localScale.x, barrel.transform.localScale.y); //} #endregion #region MISC PATHFINDING // Player ESP if (AbilityPlayerESP) { LastKnownPlayerLocation = Player; } if (path == null) return; if (Target == null) return; if (currentWaypoint >= path.vectorPath.Count) { reachedEndOfPath = true; return; } else { reachedEndOfPath = false; } #endregion // Rotate barrel towards player if (Barrel != null) // PRevents error { Quaternion targetRotation = Quaternion.Euler(new Vector3(0, 0, BarrelAngle)); Barrel.transform.rotation = Quaternion.RotateTowards(Barrel.transform.rotation, targetRotation, 200 * Time.deltaTime); } #region TARGET DETERMINATION & PATROL & PLAYER DETECTION // Check if the enemy is at the target location, used for moving to the next patrol target if (Vector2.Distance(transform.position, Target.transform.position) <= 1) { PatrolLock = false; MoveNextPatrol(); } // Patrol Countdown PatrolCooldown -= Time.deltaTime; if (PatrolCooldown <= UnityEngine.Random.Range(-3, 3)) PatrolLock = false; else PatrolLock = true; // If enemy has line of sight and is close enough the player is detected if (DetermineLineOfSight(gameObject, Player) && Vector2.Distance(transform.position, Player.transform.position) <= PlayerDetectionRange) { TargetingPlayer = true; PositionRecomputeLock = false; Primed = true; // for explosive enemies // Get the last know player location by finding which of the positions is closest to the player if (LastKnownPlayerLocation == null) { LastKnownPlayerLocation = gameObject; } foreach (GameObject pos in AllPositions) { if (Vector2.Distance(Player.transform.position, pos.transform.position) < Vector2.Distance(Player.transform.position, LastKnownPlayerLocation.transform.position)) { LastKnownPlayerLocation = pos; } } // Angle barrel towards player if (Barrel != null) { BarrelAngle = Mathf.Atan2(Player.transform.position.y - Barrel.transform.position.y, Player.transform.position.x - Barrel.transform.position.x) * Mathf.Rad2Deg; } } // Player has broken line of sight and the enemy will attempt to move to the last known location else if (LastKnownPlayerLocation != null) { if (Vector2.Distance(transform.position, LastKnownPlayerLocation.transform.position) > 0.5) { PositionRecomputeLock = true; Target = LastKnownPlayerLocation; if (AbilityTeleport && TeleportLock == false) { Teleport(); } } if (Vector2.Distance(transform.position, LastKnownPlayerLocation.transform.position) < 0.5 && DetermineLineOfSight(Player, gameObject) == false) { TargetingPlayer = false; LastKnownPlayerLocation = null; } // Reset barrel rotation BarrelAngle = 0f; } // Go back to patrol move else { Primed = false; TargetingPlayer = false; if (MoveLock == false) { MoveNextPatrol(); } // Reset barrel rotation BarrelAngle = 0f; } if (PositionRecomputeLock == false) { // Catch desired distance error if (DesiredDistance <= MinumumDistance) { DesiredDistance = MinumumDistance + 1; } // If the player moves away (CHANGE TARGET) if (Vector2.Distance(Target.transform.position, Player.transform.position) > DesiredDistance) { ComputeClosestPositionToPlayer(); } // If the player is too close to the target (CHANGE TARGET) if (Vector2.Distance(Target.transform.position, Player.transform.position) < MinumumDistance) { ComputeClosestPositionToPlayer(); } // If the target is on the other side of the player (CHANGE TARGET) //if (Vector2.Distance(transform.position, player.transform.position) < Vector2.Distance(target.transform.position, player.transform.position)) //{ // ComputeClosestPositionToPlayer(); //} // If the player is not within line of sight of the desired position (CHANGE TARGET) if (DetermineLineOfSight(Target, Player) == false) { ComputeClosestPositionToPlayer(); } if (TeleportLock == false && AbilityTeleport) { if (Vector2.Distance(transform.position, Target.transform.position) > 0.5) { Teleport(); } } // If the enemy reaches the target if (Vector2.Distance(Target.transform.position, transform.position) <= 1 && DetermineLineOfSight(gameObject, Player)) { if (EnemyType == "AIR") { ComputeClosestPositionToPlayer(); } if (TeleportLock == false && AbilityTeleport) { ComputeClosestPositionToPlayer(); Teleport(); } } } #endregion #region MOVEMENT & GROUND DETECTION //Detecting if the enemy has reached the ground if (EnemyType == "GROUND" && AbilityJump && GetComponentInChildren()) // Makes sure the enemy can jump before calculating { if (GetComponent().isGrounded == true) { JumpCooldown -= Time.deltaTime; } else { JumpCooldown = JumpCooldownReset; } } // Decriment dash timer DashCooldown -= Time.deltaTime; if (DashCooldown <= 0 && Vector2.Distance(transform.position, Target.transform.position) > 2) DashLock = false; else DashLock = true; // Call Dash if (DashLock == false && AbilityDash) { Dash(); } // Decriment teleport timer TeleportCooldown -= Time.deltaTime; if (TeleportCooldown <= 0 && Vector2.Distance(transform.position, Target.transform.position) > 2) TeleportLock = false; else TeleportLock = true; // Ground Movement if (MoveLock == false && EnemyType == "GROUND" && AbilityMove) { //FireLock = false; Vector2 direction = (Vector2)path.vectorPath[currentWaypoint] - rb.position; if (GetComponent().isGrounded == true) // Movement while grounded { if (direction.x > 0) // Move right { if (TargetingPlayer) { if (Math.Abs(rb.linearVelocity.x) < Speed * 20) // Targeting player speed Cap rb.AddForce(new Vector2(Speed * 20, rb.linearVelocity.y)); } else { if (Math.Abs(rb.linearVelocity.x) < Speed * 5) // Wandering speed Cap rb.AddForce(new Vector2(Speed * 10, rb.linearVelocity.y)); } } if (direction.x < 0) // Move left { if (TargetingPlayer) { if (Math.Abs(rb.linearVelocity.x) < Speed * 20) // Targeting player speed Cap rb.AddForce(new Vector2(-1 * Speed * 20, rb.linearVelocity.y)); } else { if (Math.Abs(rb.linearVelocity.x) < Speed * 5) // Wandering speed Cap rb.AddForce(new Vector2(-1 * Speed * 10, rb.linearVelocity.y)); } } if (direction.y > 1f) // Wants to jump { Jump(direction.x); } } else // Movement while in the air { if (direction.x > 0) // Move right { rb.AddForce(new Vector2(Speed * 2, rb.linearVelocity.y)); } if (direction.x < 0) // Move left { rb.AddForce(new Vector2(-1 * Speed * 2, rb.linearVelocity.y)); } } // A* logic float distance = Vector2.Distance(rb.position, path.vectorPath[currentWaypoint]); if (distance < nextWaypointDistance) { currentWaypoint++; } } // Air Movement if (MoveLock == false && EnemyType == "AIR" && AbilityMove) { Vector2 direction = ((Vector2)path.vectorPath[currentWaypoint] - rb.position).normalized; Vector2 force = direction * Speed * 20; rb.AddForce(force); // A* logic float distance = Vector2.Distance(rb.position, path.vectorPath[currentWaypoint]); if (distance < nextWaypointDistance) { currentWaypoint++; } } #endregion #region ATTACK // Decriment timer FireCooldown -= Time.deltaTime; if (FireCooldown <= 0 && CurrentlyReloading == false) FireLock = false; else FireLock = true; if (WeaponCurrentMagazineAmmount > 0 && FireLock == false && CurrentlyReloading == false && WeaponType != "NONE") { if (DetermineLineOfSight(gameObject, Player) == true && Vector2.Distance(transform.position, Player.transform.position) <= WeaponRange) { UseWeapon(); } } else if (WeaponCurrentMagazineAmmount == 0 && CurrentlyReloading == false) { CurrentlyReloading = true; } // Engage periodical reloading if player moves too far away and mag is low if (Vector2.Distance(transform.position, Player.transform.position) > WeaponRange + 5 && WeaponCurrentMagazineAmmount / WeaponMagazineSize < 0.75f) { ReloadCooldown = ReloadCooldownReset / 5; // Make reloading faster CurrentlyReloading = true; } // Reloading if (CurrentlyReloading) { ReloadCooldown -= Time.deltaTime; if (ReloadCooldown <= 0) { ReloadCooldown = ReloadCooldownReset; CurrentlyReloading = false; WeaponCurrentMagazineAmmount = WeaponMagazineSize; // Dynamic Relocation if (NumberOfHitsPerMag / WeaponMagazineSize < 0.5 && AbilityDynamicRelocation) DesiredDistance -= 5; NumberOfHitsPerMag = 0; } } void UseWeapon() { FireCooldown = FireCooldownReset; switch (WeaponType) { case "RANGED": // Create Projectile GameObject BulletInstance; BulletInstance = Instantiate(EnemyProjectile, Barrel.transform.position, Quaternion.LookRotation(transform.position - GameObject.FindGameObjectWithTag("Player").transform.position + new Vector3(UnityEngine.Random.Range((-1 * WeaponRandomSpread), WeaponRandomSpread), UnityEngine.Random.Range((-1 * WeaponRandomSpread), WeaponRandomSpread), 0))); //BulletInstance.transform.parent = transform; // Variables BulletInstance.GetComponent().WeaponDamage = WeaponDamage; BulletInstance.GetComponent().WeaponRange = WeaponRange; // Send it on it's way BulletInstance.GetComponent().linearVelocity = BulletInstance.transform.forward * -1 * WeaponProjectileSpeed; BulletInstance.transform.rotation = Quaternion.Euler(new Vector3(0, 0, Vector2.SignedAngle(Vector2.right, BulletInstance.transform.forward) - 90)); break; case "ROCKET": // Create Rocket GameObject Rocket; Rocket = Instantiate(EnemyProjectile, Barrel.transform.position, Quaternion.LookRotation(transform.position - GameObject.FindGameObjectWithTag("Player").transform.position + new Vector3(UnityEngine.Random.Range((-1 * WeaponRandomSpread), WeaponRandomSpread), UnityEngine.Random.Range((-1 * WeaponRandomSpread), WeaponRandomSpread), 0))); // Variables Rocket.GetComponent().WeaponDamage = WeaponDamage; Rocket.GetComponent().duration = 30; // Send it on its way Rocket.GetComponent().linearVelocity = Rocket.transform.forward * -1 * WeaponProjectileSpeed; Rocket.transform.rotation = Quaternion.Euler(new Vector3(0, 0, Vector2.SignedAngle(Vector2.right, Rocket.transform.forward) - 90)); break; case "PARTICLE": // Create particle GameObject ParticleWeapon; ParticleWeapon = Instantiate(EnemyProjectile, new Vector3(Barrel.transform.position.x + UnityEngine.Random.Range(-0.5f, 0.5f), Barrel.transform.position.y + UnityEngine.Random.Range(-0.5f, 0.5f), EnemyProjectile.transform.position.z), Quaternion.identity); ParticleWeapon.GetComponent().linearVelocity = new Vector2(transform.position.x - Player.transform.position.x + UnityEngine.Random.Range(-WeaponRandomSpread, WeaponRandomSpread), transform.position.y - Player.transform.position.y + UnityEngine.Random.Range(-WeaponRandomSpread, WeaponRandomSpread)).normalized * 1.25f * WeaponRange * -1; // Set variables ParticleWeapon.GetComponent().destroy = true; ParticleWeapon.GetComponent().opacity = true; ParticleWeapon.GetComponent().damageAmmount = WeaponDamage; break; case "MELEE": GameObject.Find("GameData").GetComponent().CurrentHealth -= WeaponDamage; break; case "GRENADE": break; default: Debug.Log("Invalid or no weapon seleted."); break; } WeaponCurrentMagazineAmmount--; } #endregion } // General Utility bool DetermineLineOfSight(GameObject object1, GameObject object2) { Vector3 RaycastStart = object1.transform.position; Vector3 RaycastDirection = (object2.transform.position - object1.transform.position).normalized; float RaycastDistance = Vector3.Distance(object2.transform.position, object1.transform.position); if (Physics2D.Raycast(RaycastStart, RaycastDirection, RaycastDistance, LayerMask.GetMask("SolidGround")) == false) { Debug.DrawRay(RaycastStart, RaycastDirection * RaycastDistance); return true; } else { return false; } } void Jump(float JumpDirection) { if (JumpCooldown <= 0 && AbilityJump) { JumpCooldown = JumpCooldownReset; rb.linearVelocity = new Vector2(rb.linearVelocity.x + JumpDirection, JumpHeight * 12); } } // Part of the pursue cycle void ComputeClosestPositionToPlayer() { MoveLock = false; if (EnemyType == "GROUND") { SuitablePositions.Clear(); foreach (GameObject query in AllPositions) { // Check the distance of the position if (Vector2.Distance(query.transform.position, Player.transform.position) < DesiredDistance && Vector2.Distance(query.transform.position, Player.transform.position) >= MinumumDistance) { // Check line of sight of the position if (DetermineLineOfSight(query, Player)) { SuitablePositions.Add(query); } } } if (SuitablePositions.Count > 0) { Target = SuitablePositions[UnityEngine.Random.Range(0, SuitablePositions.Count)]; if (AbilityTeleport == false) { foreach (GameObject pos in SuitablePositions) { //Find the point that is closest to the enemy if (Vector2.Distance(transform.position, pos.transform.position) < Vector2.Distance(transform.position, Target.transform.position)) { Target = pos; } } } } } if (EnemyType == "AIR") { //if (DetermineLineOfSight(player, gameObject)) //{ Target.transform.position = new Vector2((Player.transform.position.x + UnityEngine.Random.Range(-MinumumDistance, MinumumDistance)), (Player.transform.position.y + MinumumDistance + UnityEngine.Random.Range(-5, 0))); //} } } // Part of the patrol cycle void MoveNextPatrol() // Determine the next place to move to { if (PatrolLock == false) { PatrolCooldown = PatrolCooldownReset; PositionRecomputeLock = true; LastKnownPlayerLocation = null; DesiredDistance = WeaponRange - 5; if (EnemyType == "GROUND") { //Find Random Position nearby if (PatrolPositions.Count > 1) Target = PatrolPositions[UnityEngine.Random.Range(0, PatrolPositions.Count)]; } if (EnemyType == "AIR") { //Find Random Position nearby Target.transform.position = new Vector2((StartPosition.x + UnityEngine.Random.Range(-1 * PatrolDistance, PatrolDistance)), (StartPosition.y + UnityEngine.Random.Range(-1 * PatrolDistance, PatrolDistance))); } } } // Explode void Explode() { // Shake Camera Camera.GetComponent().shakeCamera(0.8f, 0.5f); // Create Explosion GameObject Explosion; Explosion = Instantiate(GameObject.Find("Explosion"), new Vector3(transform.position.x, transform.position.y, GameObject.Find("Explosion").transform.position.z), Quaternion.identity); Explosion.transform.rotation = Quaternion.Euler(Vector3.forward); // Set Variables Explosion.GetComponent().destroy = true; Explosion.GetComponent().opacity = true; Explosion.GetComponent().timer = 3; Explosion.GetComponent().damageAmmount = WeaponDamage; // Destroy Flying Target //if (EnemyType == "AIR") //{ // Destroy(target); //} //gameObject.GetComponent().enabled = false; // Destroy GameObject Destroy(gameObject); } void Cloak(bool state) { if (state) { gameObject.GetComponent().color = new Color(1f, 1f, 1f, HiddenTransparencyAmmount); } else { gameObject.GetComponent().color = new Color(1f, 1f, 1f, 1f); } } void Dash() { DashCooldown = DashCooldownReset; // Perform dash if (AbilityPlayerESP) rb.linearVelocity = new Vector2(transform.position.x - Player.transform.position.x, transform.position.y - Player.transform.position.y).normalized * DashSpeed * -1; else rb.linearVelocity = new Vector2(transform.position.x - Target.transform.position.x, transform.position.y - Target.transform.position.y).normalized * DashSpeed * -1; } void Teleport() { TeleportCooldown = TeleportCooldownReset; transform.position = Target.transform.position; } // On contact void OnTriggerEnter2D(Collider2D collision) { if (Primed && AbilityExplodeOnContact) { if (collision.gameObject.layer == LayerMask.NameToLayer("SolidGround") || collision.gameObject.layer == LayerMask.NameToLayer("Enemies") || collision.gameObject.layer == LayerMask.NameToLayer("Player")) { Explode(); } } } #endregion }