在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
中的代码永远不会被调用。请帮助......(并且请建议另一种编码方式,因为我知道它不是很干净)
答案 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 的脚本。所有这些都在其中实现了这些虚拟方法。
我很容易:
通过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