Do not know how to use coroutines in Unity3D

前端 未结 3 662
被撕碎了的回忆
被撕碎了的回忆 2021-02-14 16:28

In Unity3D, this is my code:

void ActivateBuff1(){
    gun.equippedGun.msPerShot /= 2;
    gun.equippedGun.shotsLeftInMag += 10;
    StartCoroutine (WaitRage ())         


        
相关标签:
3条回答
  • 2021-02-14 16:49

    Under normal circumstances, the code you've supplied should work fine. However, as determined in the comments, there's a caveat - if the Gameobject calling the coroutine is disabled/destroyed before the delay from WaitForSeconds() has completed, the coroutine will be stopped and the remaining code won't be called at all. You will either need to wait for the coroutine to finish before destroying the Gameobject, or have some other Gameobject call the coroutine.

    You mentioned that you were also looking for alternatives that might simplify your code - you might consider Invoke(), which lets you call a method after a specified delay. (As long as you're not triggering this very often, the overhead from reflection won't have an appreciable effect on your performance.) So your code could be rewritten to be somewhat shorter:

    void ActivateBuff1(){
        gun.equippedGun.msPerShot /= 2;
        gun.equippedGun.shotsLeftInMag += 10;
        Invoke("ResetPlayerRage", powerUpDuration);
    }
    
    void ActivateBuff2(){
        player.speedModifier *= 1.5f;
        Invoke("ResetPlayerSpeed", powerUpDuration);
    }
    
    void ResetPlayerRage(){
        gun.equippedGun.msPerShot *= 2;
    }
    
    void ResetPlayerSpeed(){
        player.speedModifier /= 1.5f;
    }
    

    Unfortunately, Invoke() will also be cancelled if the Gameobject is destroyed - but unlike a coroutine, it won't be cancelled if the Gameobject is disabled. So you could disable the Gameobject first (so it becomes invisible and doesn't interact with anything), then destroy it only after running the delayed method:

    void ActivateBuff1(){
        gun.equippedGun.msPerShot /= 2;
        gun.equippedGun.shotsLeftInMag += 10;
        gameObject.SetActive(false);
        Invoke("ResetPlayerRage", powerUpDuration);
    }
    
    void ResetPlayerRage(){
        gun.equippedGun.msPerShot *= 2;
        Destroy(gameObject);
    }
    

    Here's a summary of whether Invoke() and coroutines will be stopped depending on how you manipulate the script component or entire Gameobject:

    ..........................................................................
    :                                  :                     :               :
    :          Does it stop?           :   InvokeRepeating   :   Coroutine   :
    :                                  :                     :               :
    :..................................:.....................:...............:
    :                                  :                     :               :
    :   Disable the script component   :         No          :      No       :
    :                                  :                     :               :
    :..................................:.....................:...............:
    :                                  :                     :               :
    :   Destroy the script component   :         Yes         :      Yes      :
    :                                  :                     :               :
    :..................................:.....................:...............:
    :                                  :                     :               :
    :   Disable the game object        :         No          :      Yes      :
    :                                  :                     :               :
    :..................................:.....................:...............:
    :                                  :                     :               :
    :   Destroy the game object        :         Yes         :      Yes      :
    :                                  :                     :               :
    :..................................:.....................:...............:
    
    0 讨论(0)
  • 2021-02-14 16:53

    Take a look at my approach. It is using FixedUpdate method to handle timings and does not require Coroutines. Also I used Singleton Pattern in my buffers to enable easy access.

    I have a BufferBase script where I handle start/end of buffers. I can have as many buffers as I like and derive them from this class.

    BufferBase has

    • two members: _isBufferActive and _bufferRemainingTime.

    • A method named FixedUpdateBuffer which I must call in FixedUpdate of my buffers. it is responsible for timing of the buffer and calling EndBuffer() when the time is over.

    • And 3 virtual methods which I can override in my buffer classes.

    Here is the code:

    public class BufferBase : MonoBehaviour
    {
        /// <summary>
        /// Indicates whether the buffer is activated
        /// </summary>
        protected bool _isBufferActive = false;
        /// <summary>
        /// Time until buffer ends
        /// </summary>
        protected float _bufferRemainingTime = 0f;
    
    
        protected void FixedUpdateBuffer()
        {
            if (_isBufferActive)
            {
                _bufferRemainingTime -= Time.fixedDeltaTime;
                if (_bufferRemainingTime <= 0)
                {
                    EndBuffer();
                }
            }
        }
    
        /// <summary>
        /// Resets buffer
        /// </summary>
        protected void ResetBuffer()
        {
            _isBufferActive = false;
            _bufferRemainingTime = 0;
        }
    
        /// <summary>
        /// Marks the start of the buffer
        /// </summary>
        /// <param name="value"></param>
        protected virtual void StartOrExtendBuffer(float value)
        {
            //set buffer values
            _isBufferActive = true;
            _bufferRemainingTime = value;
    
            gameObject.SetActive(true);
        }
    
        /// <summary>
        /// Marks the end of buffer
        /// </summary>
        protected virtual void EndBuffer()
        {
            _bufferRemainingTime = 0;
            _isBufferActive = false;
    
            gameObject.SetActive(false);
        }
    }
    

    Now for the actual buffer. I have several scripts derived from BufferBase. All of them have those virtual methods implemented in them.

    I can easily:

    1. check whether a specific type of buffer is active or not through RageController.IsActive

    2. activate a buffer using RageController.AddRage(t) where t specifies the duration. (its duration will be reset to t each time AddRage is called)

    3. turn a buffer off using RageController.Reset()

    here is a sample buffer script:

    public class RageController : BufferBase
    {
        public static RageController instance;
    
        public static bool IsActive { get { return instance._isBufferActive; } }
    
        #region Static Methods
        internal static void AddRage(float value)
        {
            instance.StartOrExtendBuffer(value);
        }
    
        internal static void Reset()
        {
            instance.ResetBuffer();
        }
        #endregion
    
        #region Overriden Methods
        protected override void StartOrExtendBuffer(float value)
        {
            base.StartOrExtendBuffer(value);
    
            //----
            //add speed etc..
            //----
        }
    
        protected override void EndBuffer()
        {
            base.EndBuffer();
    
            //----
            //remove speed etc..
            //----
        }
        #endregion   
    
        #region Unity Methods
        void Awake()
        {
            instance = this;
        }
        void FixedUpdate()
        {
            FixedUpdateBuffer();
    
            if (_isBufferActive)
            {
                //----
                //anything that changes by time
                //----
            }
        }
        #endregion
    }
    

    Notice that at the end of RageController in FixedUpdate method you can smoothly change your desired values or enable the buffer with a delay or so by reading the value of _bufferRemainingTime

    0 讨论(0)
  • 2021-02-14 16:54

    Man, Serlite said everything.. However I would have dealt with this kind of situation differently.

    If I got it right, you setted this ActivateBuff function in the script attached to some kind of item that sets a modifier in the equipped gun and afterwards gets disabled. Instead of doing that, I would just create a Buff function in the equipped gun script(passing the modifier and the time as params) and let the gun itself to handle this.

    Since the equipped gun won't disappear, it would solve your problem and ideally could even be a more generic solution. Once you could pass through another power up which could lately generate some unexpected behavior(because there would be many scripts buffing and unbuffing the gun).

    0 讨论(0)
提交回复
热议问题