我的代码有问题吗?
public class Vaccine : MonoBehaviour
{
public GameObject pickupEffect;
public Score currentScore;
public float BonusScore;
private float immuneTime = 5f;
public PlayerMotor player;
private IEnumerator tookVaccineCo;
private void Start()
{
currentScore = FindObjectOfType<Score>();
}
void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Player"))
{
var player = other.GetComponent<PlayerMotor>();
Pickup();
TookVaccine(player);
}
}
private void TookVaccine(PlayerMotor playerMotor)
{
if (tookVaccineCo != null)
StopCoroutine(tookVaccineCo);
tookVaccineCo = TookVaccineEnum(playerMotor);
StartCoroutine(tookVaccineCo);
}
IEnumerator TookVaccineEnum(PlayerMotor playerMotor)
{
playerMotor.isImmune = true;
yield return new WaitForSeconds(immuneTime);
playerMotor.isImmune = false;
}
void Pickup()
{
currentScore.CurrentScore = currentScore.CurrentScore + BonusScore;
//apply effect for player
//remove power up object
Destroy(gameObject);
}
}发布于 2021-06-11 12:30:25
你做了
Destroy(gameObject);负责执行协程->的对象永远不会结束。
选项A-将例程移动到播放器中
例如,您可以在播放器上有一个专用的组件,如下所示
public class ImmunityController : MonoBehaviour
{
[SerializeField] private PlayerMotor _motor;
private void Awake ()
{
if(!_motor) _motor = GetComponent<PlayerMotor>();
}
private Coroutine _current;
public void SetImmune(float immuneTime)
{
if(_current) StopCoroutine(_current);
_current = StartCoroutine (ImmuneRoutine (immuneTime));
}
private IEnumerator ImmuneRoutine(float immuneTime)
{
_motor.isImmune = true;
yield return new WaitForSeconds (immuneTime);
_motor.isImmune = false;
}
}现在让你的拾取对象只启动那个例程
public class Vaccine : MonoBehaviour
{
public GameObject pickupEffect;
public Score currentScore;
public float BonusScore;
private const float immuneTime = 5f;
void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Player") && other.TryGetComponent<ImmuneController>(out var player))
{
// only tell the player that he took a vaccine - the rest is his own responsibility
player.SetImmune(immuneTime);
currentScore.CurrentScore = currentScore.CurrentScore + BonusScore
Destroy (gameObject);
}
}
}这样你就解耦了责任--我更喜欢这个选项。
选项B-仅将StartCoroutine移动到播放器
另一种选择--虽然在我看来有点“脏”--是让玩家自己执行协程,而你仍然在Vaccine脚本中实现它!
但是,这只有在确实需要“混淆”一点玩家正在执行的东西时才有意义,这样玩家对象就不会确切地知道Vaccine导致了什么。
这将是一些简单的更改,而不需要新的脚本。
在PlayerMotor中有一个附加字段
public Coroutine tookVaccineCo;然后做
public class Vaccine : MonoBehaviour
{
public GameObject pickupEffect;
public Score currentScore;
public float BonusScore;
private float immuneTime = 5f;
public PlayerMotor player;
private void Start()
{
currentScore = FindObjectOfType<Score>();
}
void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Player") && other.TryGetComponent<PlayerMotor>(out var player)
{
Pickup();
TookVaccine(player);
}
}
private void TookVaccine(PlayerMotor playerMotor)
{
// Magic! Simply let the player execute this routine without the need
// for letting him know what he is executing exactly
// For correctly stopping the routine also let the player itself
// store the currently running routine
if (player.tookVaccineCo != null) player.StopCoroutine(player.tookVaccineCo);
player.tookVaccineCo = player.StartCoroutine(TookVaccineEnum(player, immuneTime));
}
// Also this is now "shared" between all instances
// Wouldn't be necessary in the current state BUT if you are ever going to use something
// related to a specific instance of this script
// then you would have troubles -> now you simply can't do that by accident
private static IEnumerator TookVaccineEnum(PlayerMotor playerMotor, float time)
{
playerMotor.isImmune = true;
yield return new WaitForSeconds(time);
playerMotor.isImmune = false;
}
void Pickup()
{
currentScore.CurrentScore = currentScore.CurrentScore + BonusScore;
Destroy(gameObject);
}
}这样,Vaccine对象也不会有问题,因为例程本身实际上是由player对象执行的。
选项C-禁用组件并在以后销毁
只需先禁用所有相关组件即可。然后等待销毁,直到协程结束。
public class Vaccine : MonoBehaviour
{
[SerializeField] private Renderer _renderer;
[SerializeField] private Collider _collider;
public GameObject pickupEffect;
public Score currentScore;
public float BonusScore;
private float immuneTime = 5f;
public PlayerMotor player;
private Coroutine tookVaccineCo;
private void Start()
{
if(!currentScore)currentScore = FindObjectOfType<Score>();
if(!_renderer) _renderer = GetComponent<Renderer>();
if(!_collider) _collider = GetComponent<Collider>();
}
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Player") && other.TryGetComponent<PlayerMotor>(out var player))
{
Pickup();
TookVaccine(player);
}
}
private void TookVaccine(PlayerMotor playerMotor)
{
if (tookVaccineCo != null) StopCoroutine(tookVaccineCo);
tookVaccineCo = StartCoroutine(tookVaccineCo);
}
private IEnumerator TookVaccineEnum(PlayerMotor playerMotor)
{
playerMotor.isImmune = true;
yield return new WaitForSeconds(immuneTime);
playerMotor.isImmune = false;
// Now finally destroy this object
Destroy(gameObject);
}
void Pickup()
{
currentScore.CurrentScore = currentScore.CurrentScore + BonusScore;
// ONLY disable power up object
_renderer.enabled = false;
_collider.enabled = false;
}
}发布于 2021-06-11 12:16:34
您正在使用Destroy(gameObject);销毁void Pickup()中的Vaccine实例游戏对象。这似乎不是一个好主意。如果在输入触发器时执行Destroy(gameObject);:
void OnTriggerEnter(Collider other)
{
if (other.CompareTag("Player"))
{
var player = other.GetComponent<PlayerMotor>();
Pickup(); //this calls Destroy(gameObject);
TookVaccine(player);
}
}保存您的Vaccine monobehaviour实例的当前游戏对象被销毁,因此放在其中的逻辑将不再起作用。以防这可能是您遇到的问题。
我认为你需要从一个脚本中处理玩家的免疫力,这个脚本的主游戏对象不会被销毁,这个脚本处理玩家的行为(在你的例子中,认为它可能是PlayerMotor似乎是一个好主意,而不是Vaccine。
https://stackoverflow.com/questions/67931007
复制相似问题