Site Search
Homepage of Otaku No Zoku
Complete Archives of Otaku No Zoku
About Otaku No Zoku
Subscribe to Otaku No Zoku
Bookmark Otaku No Zoku

Unity scripts to charge up a weapon :

Someone on the Unity3D forums asked how to make a system for charging up a gun over time to do more damage. They want it to work like the alien guns in Halo. I gave it some thought for a couple of minutes and finally came up with the following code.

The code is broken up in to three separate scripts, you can attach them all to the same GameObject or divide them up however your scene dictates. One script explicitly handles the user’s input, another handles displaying the result, and the final script is the actual controller for making the weapon charge up. Everything important is controlled via .NET events permitting you to have multiple subscribers to each event that can respond in different ways, .e.g the GUI showing the charge up as a progress bar, and the gun simultaneously throwing off particle effects with increasing intensity as the charge up occurs.

There is a basic facility for automatically wiring up the references built in to the code, but you may have to do this by hand if

you have a strange scene architecture.

I’ve given the following code a cursory test but haven’t done a thorough and rigourous testing of it so there may be lurking bugs I am not aware of. For the most part, if you have a weapon that charges up, this code should work for you straight out of the box.

ChargeUp.cs

using UnityEngine;
     
public class ChargeUp : MonoBehaviour
{
    #region Event Declarations
 
    /// 
    /// Fired when the charge has been reset to zero
    /// 
    public event ChargeResetHandler ChargeReset;
    public delegate void ChargeResetHandler();
 
    /// 
    /// Fired when the charge is released, e.g. the player depresses the fire button
    /// 
    public event ChargeReleasedHandler ChargeReleased;
    public delegate void ChargeReleasedHandler(float charge);
 
    /// 
    /// Fired when the charge potential has reached maximum
    /// 
    public event FullyChargedHandler FullyCharged;
    public delegate void FullyChargedHandler(float charge);
    /// 
    /// Fired when the charge potential has reached sufficient energy to be useful
    /// 
    public event ReadyToReleaseHandler ReadyToRelease;
    public delegate void ReadyToReleaseHandler(float charge);
 
    /// 
    /// Fired when the charging has begun, e.g. the player depresses the charge button
    /// 
    public event ChargeStartedgHandler ChargeStarted;
    public delegate void ChargeStartedgHandler();
 
    /// 
    /// Fired when the charging has stopped, e.g. the player releases the charge button
    /// 
    public event ChargeStoppedHandler ChargeStopped;
    public delegate void ChargeStoppedHandler();
    #endregion
 
    #region Inspector Variables
    /// 
    /// Should we automatically release the charge when it reaches maximum?
    /// 
    [SerializeField]
    protected bool m_autoFireAtMaximum;
    /// 
    /// How quickly the charge will reach maximum potential, can be any positive value less than Maximum Charge Value
    /// 
    [SerializeField]
    protected float m_chargeRatePerSecond;
    /// 
    /// Maximum permitted charge value, can be any positive value
    /// 
    [SerializeField]
    protected float m_maximumChargeValue;
 
    /// 
    /// Percentage to indicate the minimum value of charge required to fire
    /// 
    [SerializeField]
    protected float m_readyToFireAt;
 
    /// 
    /// Should the charge reset when told to stop but hasn't been fired?
    /// 
    [SerializeField]
    protected bool m_resetOnStop;
 
    /// 
    /// We can start the charge value at greater than zero
    /// 
    [SerializeField]
    protected float m_startChargeValue;
 
    #endregion
 
    #region Member Variables
 
    protected readonly string m_invokeReachedFullCharge;
    protected readonly string m_invokeReachedReadyToFire;
 
    /// 
    /// When did we start tracking the charge up?
    /// 
    protected float m_startTime;
    #endregion
 
    public ChargeUp()
    {
        m_invokeReachedReadyToFire = new System.Action(ReachedReadyToFire).Method.Name;
        m_invokeReachedFullCharge = new System.Action(ReachedFullCharge).Method.Name;
    }
 
    #region Properties
    public float CurrentCharge
    {
        get
        {
            if (!IsCharging)
            {
                return (0.0f);
            }
 
            return (Mathf.Clamp(1.0f / TimeToReachMaximumFromStart * ElapsedChargeTime * MaximumCharge, 0.0f, m_maximumChargeValue));
        }
 
    }
 
    public float ElapsedChargeTime
    {
        get
        {
            if (!IsCharging)
            {
                return (0.0f);
            }
 
            return (Time.time - m_startTime);
        }
 
    }
 
    public float MaximumCharge
    {
        get
        {
            return (m_maximumChargeValue);
        }
 
        set
        {
            m_maximumChargeValue = value;
        }
 
    }
 
    public float TimeToReachReadyFromStart
    {
        get
        {
            return ((m_maximumChargeValue - m_startChargeValue) * ReadyToFireAt / m_chargeRatePerSecond);
        }
 
    }
 
    public float TimeToReachMaximumFromZero
    {
        get
        {
            return (m_maximumChargeValue / m_chargeRatePerSecond);
        }
 
    }
 
    public float TimeToReachMaximumFromStart
    {
        get
        {
            return ((m_maximumChargeValue - m_startChargeValue) / m_chargeRatePerSecond);
        }
 
    }
 
    public float TimeToReachMaximum
    {
        get
        {
            return ((m_maximumChargeValue - m_startChargeValue) / m_chargeRatePerSecond);
        }
 
    }
 
    public bool IsCharging
    {
        get;
        protected set;
    }
 
    public bool IsFull
    {
        get
        {
            return (CurrentCharge >= m_maximumChargeValue);
        }
 
    }
 
    public bool IsReady
    {
        get
        {
            return (CurrentCharge >= ReadyToFireAt);
        }
 
    }
 
    public float ReadyToFireAt
    {
        get
        {
            return (m_readyToFireAt);
        }
 
        set
        {
            m_readyToFireAt = value;
        }
 
    }
 
    #endregion
 
    #region Unity Event Handlers
    public void Awake()
    {
        System.Diagnostics.Debug.Assert(m_startChargeValue >= 0.0f && m_startChargeValue < m_maximumChargeValue);
        System.Diagnostics.Debug.Assert(m_maximumChargeValue > 0.0f);
        System.Diagnostics.Debug.Assert(m_readyToFireAt > 0.0f && m_readyToFireAt <= m_maximumChargeValue);
        System.Diagnostics.Debug.Assert(m_chargeRatePerSecond > 0.0f && m_chargeRatePerSecond < m_maximumChargeValue);
    }
 
    protected void Reset()
    {
        m_chargeRatePerSecond = 0.1f;
        m_resetOnStop = false;
        m_maximumChargeValue = 5.0f;
        m_startChargeValue = 0.0f;
        m_readyToFireAt = 0.5f;
    }
 
    #endregion
 
    #region General Member Methods
 
    public void ReleaseCharge()
    {
        if (!IsReady)
        {
            return;
        }
 
        FireChargeReleased();
        StopCharging();
    }
 
    public void StartCharging()
    {
        if (IsCharging)
        {
            return;
        }
 
        m_startTime = Time.time;
        Invoke(m_invokeReachedReadyToFire, TimeToReachReadyFromStart);
        Invoke(m_invokeReachedFullCharge, TimeToReachMaximumFromStart);
        IsCharging = true;
        FireChargeStarted();
    }
 
    public void StopCharging()
    {
        if (!IsCharging)
        {
            return;
        }
 
        CancelInvoke();
        IsCharging = false;
        FireChargeStopped();
    }
 
    public void ResetCharge()
    {
        CancelInvoke();
        IsCharging = false;
        FireChargeReset();
    }
    #endregion
 
    #region Event Dispatchers
    protected void FireChargeReleased()
    {
        if (ChargeReleased != null)
        {
            ChargeReleased(CurrentCharge);
        }
 
    }
    protected void FireChargeReset()
    {
        if (ChargeReset != null)
        {
            ChargeReset();
        }
 
    }
 
    protected void FireFullyCharged()
    {
        if (FullyCharged != null)
        {
            FullyCharged(CurrentCharge);
        }
 
    }
 
    protected void FireReadyToRelease()
    {
        if (ReadyToRelease != null)
        {
            ReadyToRelease(CurrentCharge);
        }
 
    }
 
    protected void FireChargeStarted()
    {
        if (ChargeStarted != null)
        {
            ChargeStarted();
        }
 
    }
 
    protected void FireChargeStopped()
    {
        if (ChargeStopped != null)
        {
            ChargeStopped();
        }
 
    }
    #endregion
 
    #region Invoke Handlers
    // responds to "Invoke"
    protected void ReachedFullCharge()
    {
        FireFullyCharged();
        if (m_autoFireAtMaximum)
        {
            ReleaseCharge();
        }
 
    }
 
    // responds to "Invoke"
    protected void ReachedReadyToFire()
    {
        FireReadyToRelease();
    }
    #endregion
 
}

ChargeUpInputHandler.cs


    using UnityEngine;
     
    public class ChargeUpInputHandler : MonoBehaviour
    {
        #region Inspector Variables
        [SerializeField]
        protected ChargeUp m_chargeUp;
        #endregion
     
        #region Unity Event Handlers
     
        protected void Reset()
        {
            // not efficient, but useful if you forget to set your reference in the inspector
            m_chargeUp = GameObject.FindObjectOfType(typeof(ChargeUp)) as ChargeUp;
        }
     
        protected void Update()
        {
            bool chargeEngaged = Input.GetButtonDown("Fire2");
            bool chargeDisengaged = Input.GetButtonUp("Fire2");
            if (chargeEngaged)
            {
                m_chargeUp.StartCharging();
            }
            else if (chargeDisengaged)
            {
                m_chargeUp.StopCharging();
            }
     
            bool fired = Input.GetButtonDown("Fire1");
            if (fired)
            {
                m_chargeUp.ReleaseCharge();
            }
     
        }
        #endregion
     
    }

ChargeGUI.cs


    using UnityEngine;
     
     
    public class ChargeGUI : MonoBehaviour
    {
        #region Inspector Variables
        [SerializeField]
        protected Color m_chargeFull;
     
        [SerializeField]
        protected Color m_chargeReadyToRelease;
     
        [SerializeField]
        protected ChargeUp m_chargeUp;
     
        [SerializeField]
        protected Color m_chargeNormal;
     
        [SerializeField]
        protected Color m_chargeStarted;
     
        #endregion
     
        protected Color m_colour;
     
        protected void Reset()
        {
            m_chargeNormal = Color.white;
            m_chargeStarted = Color.cyan;
            m_chargeReadyToRelease = Color.yellow;
            m_chargeFull = Color.red;
            // not efficient, but useful if you forget to set your reference in the inspector
            m_chargeUp = GameObject.FindObjectOfType(typeof(ChargeUp)) as ChargeUp;
        }
     
        protected void Start()
        {
            m_colour = m_chargeNormal;
        }
     
        protected void OnChargeReleased(float charge)
        {
            m_colour = m_chargeNormal;
            Debug.Log("Charge released " + charge);
        }
     
        protected void OnFullyCharged(float charge)
        {
            Debug.Log("Fully charged! " + charge);
            m_colour = m_chargeFull;
        }
     
        protected void OnChargeStopped()
        {
            Debug.Log("Charging stopped");
            m_colour = m_chargeNormal;
        }
     
        protected void OnChargeStarted()
        {
            Debug.Log("Charging started");
            m_colour = m_chargeStarted;
        }
     
        protected void OnReadyToRelease(float charge)
        {
            Debug.Log("Ready to fire " + charge);
            m_colour = m_chargeReadyToRelease;
        }
     
     
        #region Unity Event Handlers
     
        protected void OnDisable()
        {
            m_chargeUp.ChargeStarted -= OnChargeStarted;
            m_chargeUp.ChargeStopped -= OnChargeStopped;
            m_chargeUp.FullyCharged -= OnFullyCharged;
            m_chargeUp.ReadyToRelease -= OnReadyToRelease;
            m_chargeUp.ChargeReleased -= OnChargeReleased;
        }
     
        protected void OnEnable()
        {
            m_chargeUp.ChargeStarted += OnChargeStarted;
            m_chargeUp.ChargeStopped += OnChargeStopped;
            m_chargeUp.FullyCharged += OnFullyCharged;
            m_chargeUp.ReadyToRelease += OnReadyToRelease;
            m_chargeUp.ChargeReleased += OnChargeReleased;
        }
     
        protected void OnGUI()
        {
            GUI.color = m_colour;
            GUI.Label(new Rect(25, 25, 200, 50), "Charge: " + m_chargeUp.CurrentCharge.ToString("N2"));
            GUI.Label(new Rect(25, 75, 200, 50), "Elapsed Time: " + m_chargeUp.ElapsedChargeTime.ToString("N2"));
        }
        #endregion
     
    }
Be Sociable, Share!

Liked This Post?

Subscribe to the RSS feed or follow me on Twitter to stay up to date!