123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508 |
- using System;
- using System.Collections;
- using UnityEngine;
- public class Drone : MonoBehaviour, ILaserable
- {
- /// <summary>
- /// The Drone's Health Points.
- /// </summary>
- [Tooltip("The Drone's Health Points.")]
- public int Hitpoints = 100;
- /// <summary>
- /// How long between each laser shot.
- /// </summary>
- [Tooltip("How long between each laser shot.e")]
- public float SecondsBetweenLaserShots = 4f;
- /// <summary>
- /// Accuracy of each shot. The laser will aim at a random point in a sphere around the user. This value sets that sphere's radius.
- /// </summary>
- [Tooltip("Accuracy of each shot. The laser will aim at a random point in a sphere around the user. This value sets that sphere's radius.")]
- public float laserAccuracy = 0.5f;
- /// <summary>
- /// The object spawned when the drone shoots.
- /// </summary>
- [Tooltip("The object spawned when the drone shoots.")]
- public GameObject LaserPrefab;
- /// <summary>
- /// The object that gets spawned when the drone dies. Intended to be an explosion.
- /// </summary>
- [Tooltip("The object that gets spawned when the drone dies. Intended to be an explosion.")]
- public GameObject ExplosionPrefab;
- /// <summary>
- /// How long it takes the drone to move to a new position.
- /// </summary>
- [Tooltip("How long it takes the drone to move to a new position.")]
- public float smoothTime = 0.75f;
- /// <summary>
- /// How far a potential movement point must be from real geometry to be considered valid .
- /// Set to higher values if the drone is moving into walls or other objects.
- /// </summary>
- [Tooltip("How far a potential movement point must be from real geometry to be considered valid. " +
- "Set to higher values if the drone is moving alongside walls or other objects. ")]
- public float ClearRadius = 2f;
- /// <summary>
- /// How many times we check near a potential movement point to see if there are any obstacles around it.
- /// Higher values make it less likely a drone will move inside an object, but may cause noticeable stutter.
- /// </summary>
- [Tooltip("How many times we check near a potential movement point to see if there are any obstacles around it. " +
- "Higher values make it less likely a drone will move inside an object, but may cause noticeable stutter.")]
- public int radiusCheckRate = 100;
- /// <summary>
- /// The maximum amount of collisions detected near a movement point allowed.
- /// Higher values make it less likely for a drone to move inside an object, but too high and it may not move at all.
- /// </summary>
- [Tooltip("The maximum amount of collisions detected near a movement point allowed. " +
- "Higher values make it less likely for a drone to move inside an object, but too high and it may not move at all.")]
- public float percentageThreshold = 0.35f;
- /// <summary>
- /// Maximum angle between drone and its target to have before the drone will turn to face it.
- /// </summary>
- [Tooltip("Maximum angle between drone and its target to have before the drone will turn to face it.")]
- public float angleBeforeRotate = 20f;
- /// <summary>
- /// "AudioClips to play for the Drone. Element 0 is for its laser, 1 is for its destruction."
- /// </summary>
- [Tooltip("AudioClips to play for the Drone. Element 0 is for its laser, 1 is for its destruction.")]
- public AudioClip[] clips;
- /// <summary>
- /// Counts down the time between shots.
- /// </summary>
- private float lasershottimer;
- /// <summary>
- /// What the drone is looking/shooting at.
- /// </summary>
- private Transform target;
- /// <summary>
- /// The gun's target to rotate towards.
- /// </summary>
- private Vector3 guntarget;
- /// <summary>
- /// Reference to the audio source.
- /// </summary>
- private AudioSource audiosource;
- /// <summary>
- /// Main ZEDManager to use for determining where the drone spawns and what it attacks.
- /// If left empty, will choose the first available ZEDManager in the scene.
- /// </summary>
- [Tooltip("Main ZEDManager to use for determining where the drone spawns and what it attacks. " +
- "If left empty, will choose the first available ZEDManager in the scene. ")]
- public ZEDManager zedManager = null;
- /// <summary>
- /// The Mesh Renderer of the drone, so we can modify its material when it takes damage.
- /// </summary>
- private Renderer meshrenderer;
- /// <summary>
- /// FX for when we take damage.
- /// </summary>
- private Transform damagefx;
- /// <summary>
- /// Where we shoot the laser from.
- /// </summary>
- private Transform laseranchor;
- /// <summary>
- /// FX for when we shoot.
- /// </summary>
- private ParticleSystem muzzleflashfx;
- /// <summary>
- /// The bone that moves the drone's gun.
- /// </summary>
- private Transform dronearm;
- /// <summary>
- /// The next position that the drone needs to move to. Set only after a valid move location has been confirmed.
- /// </summary>
- private Vector3 nextposition;
- /// <summary>
- /// A reference for the SmoothDamp of the drone's movement.
- /// </summary>
- private Vector3 velocity = Vector3.zero;
- /// <summary>
- /// The light to be enabled briefly when the drone has fired.
- /// </summary>
- private Light gunlight;
- /// <summary>
- /// Whether or not we can rotate towards our target.
- /// </summary>
- private bool canrotate = false;
- /// <summary>
- /// Are we moving the drone or not.
- /// </summary>
- private bool canchangerotation = false;
- /// <summary>
- /// Link with the object that spawned this instance. Used to clear the spawner's reference in OnDestroy().
- /// </summary>
- private DroneSpawner spawner;
- private float damageFlashAmount
- {
- #if !ZED_HDRP || !ZED_URP
- get
- {
- return meshrenderer.material.GetColor("_Color").a;
- }
- set
- {
- Color newcol = meshrenderer.material.GetColor("_Color");
- newcol.a = value;
- meshrenderer.material.SetColor("_Color", newcol);
- }
- #elif ZED_HDRP
- get
- {
- return meshrenderer.material.GetColor("_BaseColor").a;
- }
- set
- {
- Color newcol = meshrenderer.material.GetColor("_BaseColor");
- newcol.a = value;
- meshrenderer.material.SetColor("_BaseColor", newcol);
- }
- #elif ZED_URP
- get
- {
- return meshrenderer.material.GetColor("_BaseColor").a;
- }
- set
- {
- Color newcol = meshrenderer.material.GetColor("_BaseColor");
- newcol.a = value;
- meshrenderer.material.SetColor("_BaseColor", newcol);
- }
- #endif
- }
- // Use this for initialization
- void Start ()
- {
- //Cache the ZED's left camera for occlusion testing purposes
- if (zedManager==null)
- zedManager = FindObjectOfType<ZEDManager>();
- // Set the default position of the Drone to the one he spawned at.
- nextposition = transform.position;
- //Set the countdown timer to fire a laser
- lasershottimer = SecondsBetweenLaserShots;
- // Set the audio source.
- audiosource = GetComponent<AudioSource>();
- if(audiosource != null && clips.Length > 2)
- {
- audiosource.clip = clips[2];
- audiosource.volume = 1f;
- audiosource.Play();
- }
- // If these variables aren't set, look for their objects by their default name.
- Transform[] children = transform.GetComponentsInChildren<Transform>();
- foreach (var child in children) {
- if (child.name == "Drone_Mesh")
- meshrenderer = child.GetComponent<Renderer>();
- else if (child.name == "MuzzleFlash_FX")
- muzzleflashfx = child.GetComponent<ParticleSystem>();
- else if (child.name == "Damage_FX")
- damagefx = child;
- else if (child.name == "Laser_Anchor")
- laseranchor = child;
- else if (child.name == "Gun_Arm")
- dronearm = child;
- else if (child.name == "Point_Light")
- gunlight = child.GetComponent<Light>();
- }
- //If the _target isn't set, set it to the PlayerDamageReceiver, assuming there is one in the scene.
- if(!target)
- {
- target = FindObjectOfType<PlayerDamageReceiver>().transform;
- guntarget = target.position;
- }
- }
-
- void Update ()
- {
- //If we've flashed the damage material, lower the blend amount.
- if (damageFlashAmount > 0)
- {
- float tmp = damageFlashAmount;
- tmp -= Time.deltaTime / 1.5f;
- if (tmp < 0)
- tmp = 0;
- damageFlashAmount = tmp;
- }
- //Enabling damage FX based on HitPoints left.
- switch (Hitpoints) {
- case 80:
- damagefx.GetChild (0).gameObject.SetActive (true);
- break;
- case 50:
- damagefx.GetChild (1).gameObject.SetActive (true);
- break;
- case 20:
- damagefx.GetChild (2).gameObject.SetActive (true);
- break;
- }
- //If its time to can change our position...
- if (canchangerotation)
- {
- //...then look for a new one until it's a valid location.
- if (FindNewMovePosition (out nextposition))
- {
- canchangerotation = false;
- }
- }
- //Count down the laser shot timer. If zero, fire and reset it.
- lasershottimer -= Time.deltaTime;
- if (lasershottimer <= 0f && target != null)
- {
- //Apply a degree of accuracy based on the drone distance from the player
- //Take a random point on a radius around the Target's position. That radius becomes smaller as the target is closer to us.
- Vector3 randompoint = UnityEngine.Random.onUnitSphere * (laserAccuracy * (Vector3.Distance(target.position, transform.position) / (spawner.maxSpawnDistance / 2))) + target.position;
- //Check if the chosen point is closer to the edge of the camera. We dont want any projectile coming straight in the players eyes.
- if (randompoint.z >= target.position.z + 0.15f || randompoint.z <= target.position.z - 0.15f)
- {
- guntarget = randompoint;
- //Firing the laser
- FireLaser(randompoint);
- //Reseting the timer.
- lasershottimer = SecondsBetweenLaserShots;
- }
- }
- //Drone Movement & Rotation
- if (target != null)
- {
- //Get the direction to the target.
- Vector3 targetDir = target.position - transform.position;
- //Get the angle between the drone and the target.
- float angle = Vector3.Angle(targetDir, transform.forward);
- //Turn the drone to face the target if the angle between them if greater than...
- if (angle > angleBeforeRotate && canrotate == false)
- {
- canrotate = true;
- }
- if (canrotate == true) {
- var newRot = Quaternion.LookRotation (target.transform.position - transform.position);
- transform.rotation = Quaternion.Lerp (transform.rotation, newRot, Time.deltaTime * 2f);
- if (angle < 5 && canrotate == true)
- {
- canrotate = false;
- }
- }
- //Rotate the drone's gun to always face the target.
- dronearm.rotation = Quaternion.LookRotation (guntarget - dronearm.position);
- }
- //Simply moving nextposition to something other than transform.position will cause it to move.
- if (transform.position != nextposition)
- {
- transform.position = Vector3.SmoothDamp(transform.position, nextposition, ref velocity, smoothTime);
- }
- }
-
- /// <summary>
- /// Fires the laser.
- /// </summary>
- private void FireLaser(Vector3 randompoint)
- {
- if (audiosource.clip != clips[0])
- {
- audiosource.clip = clips[0];
- audiosource.volume = 0.2f;
- }
- //Creat a laser object
- GameObject laser = Instantiate(LaserPrefab);
- laser.transform.position = laseranchor.transform.position;
- laser.transform.rotation = Quaternion.LookRotation(randompoint - laseranchor.transform.position);
- //Play the Particle effect.
- muzzleflashfx.Play();
- //Play a sound
- if (audiosource)
- {
- audiosource.Play();
- }
- //MuzzleFlashLight
- StartCoroutine(FireLight());
- //StartRelocatingDrone
- StartCoroutine(RelocationDelay());
- }
- /// <summary>
- /// Forces a delay before moving, and then only allows rotation if the drone has reached its destination.
- /// </summary>
- /// <returns></returns>
- IEnumerator RelocationDelay()
- {
- yield return new WaitForSeconds(1f);
- //Allow another relocation if we have already reached the current nextposition.
- if (Vector3.Distance(transform.position, nextposition) <= 0.1f)
- {
- canchangerotation = true;
- }
- }
- /// <summary>
- /// What happens when the drone gets damaged. In the ZED drone demo, Lasershot_Player calls this.
- /// </summary>
- /// <param name="damage">Damage.</param>
- void ILaserable.TakeDamage(int damage)
- {
- //Remove hitpoints as needed
- Hitpoints -= damage;
- //Blend the materials to make it take damage
- //meshrenderer.material.SetFloat("_Blend", 1);
- damageFlashAmount = 1f;
- //Destroy if it's health is below zero
- if (Hitpoints <= 0)
- {
- //Add time to prevent laser firing while we die.
- lasershottimer = 99f;
- if(spawner) spawner.ClearDrone();
- if (ExplosionPrefab)
- {
- Instantiate(ExplosionPrefab, transform.position, Quaternion.identity);
- }
- if (audiosource != null && clips.Length > 1)
- {
- audiosource.Stop();
- audiosource.clip = clips[1];
- audiosource.volume = 1f;
- audiosource.Play();
- }
- StartCoroutine(DestroyDrone());
- return;
- }
- }
- /// <summary>
- /// Plays explosion FX, then destroys the drone.
- /// </summary>
- /// <returns></returns>
- IEnumerator DestroyDrone()
- {
- transform.GetChild(0).gameObject.SetActive(false);
- transform.GetChild(1).gameObject.SetActive(false);
- yield return new WaitForSeconds(3f);
- Destroy(gameObject);
- }
- /// <summary>
- /// Checks nearby for valid places for the drone to move.
- /// Valid places must be in front of the player, and not intersect any objects within a reasonable tolerance.
- /// Use radiusCheckRate and percentageThreshold to tweak what counts as a valid location.
- /// </summary>
- /// <param name="newpos"></param>
- /// <returns></returns>
- private bool FindNewMovePosition(out Vector3 newpos)
- {
- //We can't move if the ZED isn't initialized.
- if (!zedManager.IsZEDReady)
- {
- newpos = transform.position;
- return false;
- }
- Vector3 randomPosition;
- // Look Around For a New Position
- //If the Drone is on the screen, search around a smaller radius.
- //Note that we only check the primary ZEDManager because we only want the drone to spawn in front of the player anyway.
- if (ZEDSupportFunctions.CheckScreenView(transform.position, zedManager.GetMainCamera()))
- randomPosition = UnityEngine.Random.onUnitSphere * UnityEngine.Random.Range(2f, 3f) + transform.position;
- else //if the drone is outside, look around a bigger radius to find a position which is inside the screen.
- randomPosition = UnityEngine.Random.onUnitSphere * UnityEngine.Random.Range(4f, 5f) + transform.position;
- // Look For Any Collisions Through The ZED
- bool hit = ZEDSupportFunctions.HitTestAtPoint(zedManager.zedCamera, zedManager.GetMainCamera(), randomPosition);
- if (!hit)
- {
- newpos = transform.position;
- return false;
- }
- //If we spawn the drone at that world point, it'll spawn inside a wall. Bring it closer by a distance of ClearRadius.
- Quaternion directiontoDrone = Quaternion.LookRotation(zedManager.GetMainCameraTransform().position - randomPosition, Vector3.up);
- Vector3 newPosition = randomPosition + directiontoDrone * Vector3.forward * ClearRadius;
- //Check the new position isn't too close from the camera.
- float dist = Vector3.Distance(zedManager.GetMainCamera().transform.position, randomPosition);
- if (dist < 1f)
- {
- newpos = transform.position;
- return false;
- }
- //Also check nearby points in a sphere of radius to make sure the whole drone has a clear space.
- if (ZEDSupportFunctions.HitTestOnSphere(zedManager.zedCamera, zedManager.GetMainCamera(), newPosition, 1f, radiusCheckRate, percentageThreshold))
- {
- newpos = transform.position;
- return false;
- }
- //Return true if it's made it this far and out the location we chose.
- newpos = newPosition;
- return true;
- }
- /// <summary>
- /// Sets a reference to the Drone spawner governing its spawning.
- /// Used to notify the spawner when it's destroyed.
- /// </summary>
- /// <param name="spawner">Reference to the scene's DroneSpawner component.</param>
- public void SetMySpawner(DroneSpawner spawner)
- {
- this.spawner = spawner;
- }
- /// <summary>
- /// Turns on the drone gun's light briefly to simulate a muzzle flash. Because lasers totally have muzzle flashes.
- /// </summary>
- /// <returns></returns>
- IEnumerator FireLight()
- {
- gunlight.enabled = true;
- yield return new WaitForSeconds(0.15f);
- gunlight.enabled = false;
- }
- }
|