using Unity.FPS.Game; using UnityEngine; using UnityEngine.Events; namespace Unity.FPS.Gameplay { [RequireComponent(typeof(AudioSource))] public class Jetpack : MonoBehaviour { [Header("References")] [Tooltip("Audio source for jetpack sfx")] public AudioSource AudioSource; [Tooltip("Particles for jetpack vfx")] public ParticleSystem[] JetpackVfx; [Header("Parameters")] [Tooltip("Whether the jetpack is unlocked at the begining or not")] public bool IsJetpackUnlockedAtStart = false; [Tooltip("The strength with which the jetpack pushes the player up")] public float JetpackAcceleration = 7f; [Range(0f, 1f)] [Tooltip( "This will affect how much using the jetpack will cancel the gravity value, to start going up faster. 0 is not at all, 1 is instant")] public float JetpackDownwardVelocityCancelingFactor = 1f; [Header("Durations")] [Tooltip("Time it takes to consume all the jetpack fuel")] public float ConsumeDuration = 1.5f; [Tooltip("Time it takes to completely refill the jetpack while on the ground")] public float RefillDurationGrounded = 2f; [Tooltip("Time it takes to completely refill the jetpack while in the air")] public float RefillDurationInTheAir = 5f; [Tooltip("Delay after last use before starting to refill")] public float RefillDelay = 1f; [Header("Audio")] [Tooltip("Sound played when using the jetpack")] public AudioClip JetpackSfx; bool m_CanUseJetpack; PlayerCharacterController m_PlayerCharacterController; PlayerInputHandler m_InputHandler; float m_LastTimeOfUse; // stored ratio for jetpack resource (1 is full, 0 is empty) public float CurrentFillRatio { get; private set; } public bool IsJetpackUnlocked { get; private set; } public bool IsPlayergrounded() => m_PlayerCharacterController.IsGrounded; public UnityAction OnUnlockJetpack; void Start() { IsJetpackUnlocked = IsJetpackUnlockedAtStart; m_PlayerCharacterController = GetComponent(); DebugUtility.HandleErrorIfNullGetComponent(m_PlayerCharacterController, this, gameObject); m_InputHandler = GetComponent(); DebugUtility.HandleErrorIfNullGetComponent(m_InputHandler, this, gameObject); CurrentFillRatio = 1f; AudioSource.clip = JetpackSfx; AudioSource.loop = true; } void Update() { // jetpack can only be used if not grounded and jump has been pressed again once in-air if (IsPlayergrounded()) { m_CanUseJetpack = false; } else if (!m_PlayerCharacterController.HasJumpedThisFrame && m_InputHandler.GetJumpInputDown()) { m_CanUseJetpack = true; } // jetpack usage bool jetpackIsInUse = m_CanUseJetpack && IsJetpackUnlocked && CurrentFillRatio > 0f && m_InputHandler.GetJumpInputHeld(); if (jetpackIsInUse) { // store the last time of use for refill delay m_LastTimeOfUse = Time.time; float totalAcceleration = JetpackAcceleration; // cancel out gravity totalAcceleration += m_PlayerCharacterController.GravityDownForce; if (m_PlayerCharacterController.CharacterVelocity.y < 0f) { // handle making the jetpack compensate for character's downward velocity with bonus acceleration totalAcceleration += ((-m_PlayerCharacterController.CharacterVelocity.y / Time.deltaTime) * JetpackDownwardVelocityCancelingFactor); } // apply the acceleration to character's velocity m_PlayerCharacterController.CharacterVelocity += Vector3.up * totalAcceleration * Time.deltaTime; // consume fuel CurrentFillRatio = CurrentFillRatio - (Time.deltaTime / ConsumeDuration); for (int i = 0; i < JetpackVfx.Length; i++) { var emissionModulesVfx = JetpackVfx[i].emission; emissionModulesVfx.enabled = true; } if (!AudioSource.isPlaying) AudioSource.Play(); } else { // refill the meter over time if (IsJetpackUnlocked && Time.time - m_LastTimeOfUse >= RefillDelay) { float refillRate = 1 / (m_PlayerCharacterController.IsGrounded ? RefillDurationGrounded : RefillDurationInTheAir); CurrentFillRatio = CurrentFillRatio + Time.deltaTime * refillRate; } for (int i = 0; i < JetpackVfx.Length; i++) { var emissionModulesVfx = JetpackVfx[i].emission; emissionModulesVfx.enabled = false; } // keeps the ratio between 0 and 1 CurrentFillRatio = Mathf.Clamp01(CurrentFillRatio); if (AudioSource.isPlaying) AudioSource.Stop(); } } public bool TryUnlock() { if (IsJetpackUnlocked) return false; OnUnlockJetpack.Invoke(true); IsJetpackUnlocked = true; m_LastTimeOfUse = Time.time; return true; } } }