using System.Collections.Generic; using UnityEngine; using UnityEngine.Events; namespace Unity.Cinemachine.Samples { /// /// This object manages player shooting. It is expected to be on the player object, /// or on a child SimplePlayerAimController object of the player. /// /// If an AimTargetManager is specified, then the player will aim at that target. /// Otherwise, the player will aim in the forward direction of the player object, /// or of the SimplePlayerAimController object if it exists and is not decoupled /// from the player rotation. /// class SimplePlayerShoot : MonoBehaviour, IInputAxisOwner { [Tooltip("The bullet prefab to instantiate when firing")] public GameObject BulletPrefab; [Tooltip("Maximum bullets per second")] public float MaxBulletsPerSec = 10; [Tooltip("Input Axis for firing. Value is 0 or 1")] public InputAxis Fire = InputAxis.DefaultMomentary; [Tooltip("Target to Aim towards. If null, the aim is defined by the forward vector of this gameObject.")] public AimTargetManager AimTargetManager; [Tooltip("Event that's triggered when firing.")] public UnityEvent FireEvent; float m_LastFireTime; SimplePlayerAimController AimController; // We pool the bullets for improved performance readonly List m_BulletPool = new (); /// Report the available input axes to the input axis controller. /// We use the Input Axis Controller because it works with both the Input package /// and the Legacy input system. This is sample code and we /// want it to work everywhere. void IInputAxisOwner.GetInputAxes(List axes) { axes.Add(new () { DrivenAxis = () => ref Fire, Name = "Fire" }); } void OnValidate() { MaxBulletsPerSec = Mathf.Max(1, MaxBulletsPerSec); } void Start() { TryGetComponent(out AimController); } void Update() { var now = Time.time; bool fireNow = BulletPrefab != null && now - m_LastFireTime > 1 / MaxBulletsPerSec && Fire.Value > 0.1f; // Get the firing direction. Special case: if there is a decoupled AimController, // firing direction is character forward, not AimController forward. var fwd = transform.forward; bool decoupled = AimController != null && AimController.PlayerRotation == SimplePlayerAimController.CouplingMode.Decoupled; if (decoupled) fwd = transform.parent.forward; // Face the firing direction if appropriate if (AimController != null && !decoupled) { var rotationTime = AimController.RotationDamping; if (fireNow || now - m_LastFireTime <= rotationTime) AimController.RecenterPlayer(rotationTime); } // Fire the bullet if (fireNow) { m_LastFireTime = now; if (AimTargetManager != null) fwd = AimTargetManager.GetAimDirection(transform.position, fwd).normalized; // Because creating and destroying GameObjects is costly, we pool them and recycle // the deactivated ones. The bullets deactivate themselves after a time. GameObject bullet = null; for (var i = 0; bullet == null && i < m_BulletPool.Count; ++i) // Look in the pool if one is available { if (!m_BulletPool[i].activeInHierarchy) { bullet = m_BulletPool[i]; m_BulletPool.Remove(bullet); } } // Instantiate a new bullet if none are found in the pool if (bullet == null) bullet = Instantiate(BulletPrefab); // Off it goes! m_BulletPool.Add(bullet); bullet.transform.SetPositionAndRotation( transform.position + fwd, Quaternion.LookRotation(fwd, transform.up)); bullet.SetActive(true); FireEvent.Invoke(); } } } }