不知道如何在Unity3D中使用协同程序

时间:2016-11-24 18:11:57

标签: c# unity3d

在Unity3D中,这是我的代码:

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

void ActivateBuff2(){
    player.speedModifier *= 1.5f;
    StartCoroutine (WaitSpeed ());
}

IEnumerator WaitRage(){
    yield return new WaitForSeconds(powerUpDuration);
    gun.equippedGun.msPerShot *= 2;
}

IEnumerator WaitSpeed(){
    yield return new WaitForSeconds(powerUpDuration);
    player.speedModifier /= 1.5f;
}

每次玩家进入加电状态时,都会调用其中一个ActivateBuff方法。显然,powerUps效果不会永远持续,所以我在等待一定秒数后使用IEnumerators来反转原始方法的效果。但是出于某种原因,IEnumerators中的代码永远不会被调用。请帮助......(并且请建议另一种编码方式,因为我知道它不是很干净)

3 个答案:

答案 0 :(得分:12)

在正常情况下,您提供的代码应该可以正常工作。但是,正如评论中所确定的那样,如果在WaitForSeconds()的延迟完成之前,如果Gameobject调用协程已被禁用/销毁,则会有一个警告 - 协程将被停止< / strong>并且根本不会调用剩余的代码。在破坏Gameobject之前你需要等待协程完成,或者让其他游戏对象调用协程。

您提到您还在寻找可能简化代码的替代方案 - 您可以考虑Invoke(),它允许您在指定的延迟后调用方法。 (只要你不经常触发这种情况,反射的开销就不会对你的性能产生明显的影响。)所以你的代码可以重写得更短:

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;
}

不幸的是,如果Gameobject被销毁,Invoke()也将被取消 - 但与协程不同,如果Gameobject被禁用,它将不会被取消。所以你可以先禁用Gameobject(因此它变得不可见并且不与任何东西交互),然后在运行延迟方法之后将其销毁:

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

void ResetPlayerRage(){
    gun.equippedGun.msPerShot *= 2;
    Destroy(gameObject);
}

以下是根据您操作脚本组件或整个Gameobject的方式来停止Invoke()和协同程序的摘要:

..........................................................................
:                                  :                     :               :
:          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      :
:                                  :                     :               :
:..................................:.....................:...............:

答案 1 :(得分:2)

Man,Serlite说了一切......但是我会以不同的方式处理这种情况。

如果我做对了,你在附加到某种物品的脚本中设置了这个ActivateBuff功能,该物品在装备好的枪中设置一个修饰符,然后被禁用。而不是这样做,我只是在装备好的枪脚本中创建一个Buff函数(将修饰符和时间作为参数传递)并让枪本身来处理它。

由于配备的枪不会消失,它可以解决您的问题,理想情况下甚至可以是更通用的解决方案。一旦你可以通过另一个可能最近产生一些意外行为的电源(因为会有许多脚本缓冲和解除枪支)。

答案 2 :(得分:1)

看看我的方法。它使用 FixedUpdate 方法来处理时序,不需要Coroutines。我还在缓冲区中使用了Singleton Pattern来实现轻松访问。

我有一个 BufferBase 脚本,我处理缓冲区的开始/结束。我可以拥有尽可能多的缓冲区,并从这个类中派生出来。

BufferBase

  • 两位成员:_isBufferActive_bufferRemainingTime

  • 一个名为FixedUpdateBuffer的方法,我必须在我的缓冲区的 FixedUpdate 中调用它。它负责缓冲区的计时,并在时间结束时调用EndBuffer()

  • 我可以在缓冲区类中覆盖3个虚拟方法。

以下是代码:

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);
    }
}

现在为实际的缓冲区。我有几个源自 BufferBase 的脚本。所有这些都在其中实现了这些虚拟方法。

我很容易:

  1. 通过RageController.IsActive

  2. 检查特定类型的缓冲区是否有效
  3. 使用RageController.AddRage(t)激活缓冲区,其中t指定持续时间。 (每次调用AddRage时,其持续时间将重置为t)

  4. 使用RageController.Reset()

  5. 关闭缓冲区

    这是一个示例缓冲脚本:

    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
    }
    

    请注意,在FixedUpdate方法的RageController末尾,您可以通过读取_bufferRemainingTime

    的值,平滑地更改所需的值或延迟启用缓冲区