不知道如何在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的代码永远不会被调用。 请帮助…(并请建议另一种编码方式,因为我知道它不是很干净)

在正常情况下,您提供的代码应该可以正常工作。 但是,正如评论中所确定的那样,有一点需要注意 – 如果在 WaitForSeconds()延迟完成之前调用协程的Gameobject被禁用/销毁协程将被停止 ,其余的代码将不会被调用。 在破坏Gameobject之前你需要等待协程完成,或者让其他一些Gameobject调用协程。

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

 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()和协同程序的摘要:

 .................................................. ........................
 ::::
 :它会停止吗?  :InvokeRepeating:Coroutine:
 ::::
 :..................................:.............. .......:...............:
 ::::
 :禁用脚本组件:否:否:
 ::::
 :..................................:.............. .......:...............:
 ::::
 :销毁脚本组件:是:是:
 ::::
 :..................................:.............. .......:...............:
 ::::
 :禁用游戏对象:否:是:
 ::::
 :..................................:.............. .......:...............:
 ::::
 :销毁游戏对象:是:是:
 ::::
 :..................................:.............. .......:...............:

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

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

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

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

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

BufferBase

  • 两个成员: _isBufferActive_bufferRemainingTime

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

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

这是代码:

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

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

我很容易:

  1. 通过RageController.IsActive检查特定类型的缓冲区是否处于活动状态

  2. 使用RageController.AddRage(t)激活缓冲区,其中t指定持续时间。 (每次调用AddRage其持续时间将重置为t)

  3. 使用RageController.Reset()关闭缓冲区

这是一个示例缓冲脚本:

 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的值平滑地更改所需的值或启用缓冲区