Home > database >  Passing reference to a variable in another object
Passing reference to a variable in another object

Time:02-02

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));
  •  Tags:  
  • Related