不知道如何在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的脚本。 所有这些都在其中实现了这些虚拟方法。
我很容易:
-
通过
RageController.IsActive
检查特定类型的缓冲区是否处于活动状态 -
使用
RageController.AddRage(t)
激活缓冲区,其中t指定持续时间。 (每次调用AddRage
其持续时间将重置为t) -
使用
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
的值平滑地更改所需的值或启用缓冲区