I am trying to learn unity. This is something I would use in Javascript so I was hoping to find a way to do this in C#, where you take the collision variable and use the reference to have another function clean up the variables after you are finished.
Edit
I have been practicing since posting this, I am trying to pass a reference to another class to handle the turn on and off of the variable.
private void OnTriggerEnter(Collider collision) {
if(collision.gameObject.CompareTag("Enemy")) {
PowerUpRoutine(ref collision.gameObject.GetComponent<enemy>().powerup);
Destroy(gameObject);
}
if (collision.gameObject.CompareTag("Player")) {
PowerUpRoutine(ref collision.gameObject.GetComponent<player>().powerup);
Destroy(gameObject);
}
}
IEnumerator PowerUpRoutine(float target) {
target = 1;
yield return new WaitForSeconds(5);
target = 0;
}
I have also tried not passing powerup, but just the object and it still errors. Is there anyway to accomplish this?
Original
private void OnTriggerEnter(Collider collision) {
if(collision.gameObject.CompareTag("Enemy")) {
collision.gameObject.GetComponent<enemy>().powerup = 1;
StartCoroutine(removePower(collision.gameObject.GetComponent<enemy>()));
Destroy(gameObject);
}
if (collision.gameObject.CompareTag("Player")) {
collision.gameObject.GetComponent<player>().powerup = 1;
StartCoroutine(removePower(collision.gameObject.GetComponent<player>()));
Destroy(gameObject);
}
}
IEnumerator removePower(GameObject target) {
yield return new WaitForSeconds(5);
target.powerup = 0;
}
CodePudding user response:
First if all there is a general flaw in your approach: When you
Destroy(gameObject);
this object this component is attached to then also all Coroutines are immediately canceled.
I would therefore start the coroutine rather on the target itself (see example below).
And then your classes should have a common interface or base class like for example
Solution 1 - Common Base Class
public abstract class Character : MonoBehaviour
{
public float powerup;
// And other common members
}
And then you inherit
public class Player : Character
{
// Additional player specific stuff
}
and
public class Enemy : Character
{
// Additional enemy specific stuff
}
Solution 2 - Common Interface
if you rather want to go for an interface
public interface ICharacter
{
float powerup { get; set; }
}
And then both your classes have to implement that
public class Player : MonoBehaviour, ICharacter
{
public float powerup { get; set; }
// Additional player specific stuff
}
and
public class Enemy : MonoBehaviour, ICharacter
{
public float powerup { get; set; }
// Additional player specific stuff
}
And then your collision code could simply be
private void OnTriggerEnter(Collider collision)
{
// TryGetComponent now finds anything inherited from Character
// you don't even need to check the tags
if(collision.TryGetComponent<Character>(out var character))
// Or if using the interface
//if(collision.TryGetComponent<ICharacter>(out var character)
{
// Let the character run this coroutine without even having to know what it does
character.StartCoroutine(PowerUpRoutine(character));
// This is now ok and not terminating the Coroutine since the routine is run by character itself
Destroy(gameObject);
}
}
IEnumerator PowerUpRoutine(Character target)
// or if using the interface accordingly
//IEnumerator PowerUpRoutine(ICharacter target)
{
// Have in mind though that this routine is now running on a different object
// at a time where this component is already destroyed => You can't reference to anything of this component in here!
target.powerup = 1;
yield return new WaitForSeconds(5);
target.powerup = 0;
}
Solution 3 - Simple Overload
Or as last resort if the before two is not an option for whatever reason you could of course also simply have two overloads
IEnumerator PowerUpRoutine(Player target)
{
target.powerup = 1;
yield return new WaitForSeconds(5);
target.powerup = 0;
}
IEnumerator PowerUpRoutine(Enemy target)
{
target.powerup = 1;
yield return new WaitForSeconds(5);
target.powerup = 0;
}
and then do
private void OnTriggerEnter(Collider collision)
{
if(collision.TryGetComponent<Player>(out var player))
{
player.StartCoroutine(PowerUpRoutine(player));
Destroy(gameObject);
}
else if(collision.TryGetComponent<Enemy>(out var enemy))
{
enemy.StartCoroutine(PowerUpRoutine(enemy));
Destroy(gameObject);
}
}
CodePudding user response:
Note: Coroutines are also stopped when the MonoBehaviour is destroyed or if the GameObject the MonoBehaviour is attached to is disabled.
You should destroy the gameobject when the coroutine finishes.
StartCoroutine(removePower(collision.gameObject.GetComponent<enemy>()));
//Destroy(gameObject);
IEnumerator removePower(GameObject target) {
yield return new WaitForSeconds(5);
target.powerup = 0;
Destroy(gameObject);
}
Or run the coroutine on another gameobject
var enemy = collision.gameObject.GetComponent<enemy>();
enemy.StartCoroutine(removePower(enemy));
