C# how to make a script talk to a Coroutine in another script

Hey there, First of all I’m a newbie when it comes to programming. I’ve been following a tutorial and completed it, but I want to add some more features to the game. Right now I have a Sidescrolling Shooter with a Ship that shoots enemies. I got the enemies to return fire, make the collision work, but I cant seem to fix my missile script calling on my ship script to execute its coroutine. Answers will be much appreciated

Here is my Player Script:

using UnityEngine;
using System.Collections;

public class Player : MonoBehaviour 
{

	enum State
	{
		Playing,
		Explosion,
		Invincible
	}
	private State state = State.Playing;
	
	public float PlayerSpeed;
	public GameObject ProjectilePrefab;
	public GameObject ExplosionPrefab;
	
	public static int Score = 0;
	public static int Lives = 3;
	public static int Missed = 0;
	
	private float ProjectileOffset = 1.25f;
	private float shipInvisibleTime = 1.5f;
	private float shipMoveOnToScreenSpeed = 5;
	private float blinkRate = .1f;
	private int numberOfTimesToBlink = 10;
	private int blinkCount;
	private int shootLimiter = 0;
	
	void Update () 
	{
		if (state != State.Explosion)
		{
			shootLimiter ++;
			float amtToMove = Input.GetAxisRaw("Vertical") * PlayerSpeed * Time.deltaTime;
			transform.Translate(Vector3.up * amtToMove);
		
		
			if (transform.position.y <= -4.5f)
				transform.position = new Vector3 (transform.position.x, 6f, transform.position.z);
			else if (transform.position.y >= 6f)
				transform.position = new Vector3 (transform.position.x, -4.5f, transform.position.z);
		
			if (Input.GetKeyDown("space") && shootLimiter >= 70)
			{
				Vector3 position = new Vector3(transform.position.x + ProjectileOffset, transform.position.y);
				Instantiate(ProjectilePrefab, position, Quaternion.identity);
				shootLimiter = 0;
			}
		}
	}
	void OnGUI() 
	{
		GUI.Label(new Rect(10, 10, 120, 20), "Score: " + Player.Score.ToString());
		GUI.Label(new Rect(10, 30, 60, 20), "Lives: " + Player.Lives.ToString());
		GUI.Label(new Rect(10, 50, 120, 20), "Missed: " + Player.Missed.ToString());
	}
	void OnTriggerEnter(Collider otherObject)
	{
		if(otherObject.tag == "enemy" && state == State.Playing)
		{
			Player.Lives--;
			
			Enemy enemy = (Enemy)otherObject.gameObject.GetComponent("Enemy");
			enemy.SetPositionAndSpeed();
			
			StartCoroutine(DestroyShip());
		}
	}
	
	IEnumerator DestroyShip()
	{
		state = State.Explosion;
		Instantiate (ExplosionPrefab,  transform.position, Quaternion.identity);
		gameObject.renderer.enabled = false;
		transform.position = new Vector3(-7.8f, transform.position.z, transform.position.z);
		yield return new WaitForSeconds(shipInvisibleTime);
		if (Player.Lives > 0)
		{
			gameObject.renderer.enabled = true;
			
			while (transform.position.x <= -5.4)
			{
				float amtToMove = shipMoveOnToScreenSpeed * Time.deltaTime;
				transform.position = new Vector3(transform.position.x + amtToMove, 0f, transform.position.z);
				
				yield return 0;
			}
			state = State.Invincible;
			
			while (blinkCount < numberOfTimesToBlink)
			{
				gameObject.renderer.enabled = !gameObject.renderer.enabled;
				
				if (gameObject.renderer.enabled == true)
					blinkCount++;
				
					yield return new WaitForSeconds(blinkRate);
			}
			blinkCount = 0;
			state = State.Playing;
		}
		else
			Application.LoadLevel(2);
	}
}

Here is my Enemy’s missile script (its messy I know)

using UnityEngine;

using System.Collections;

public class EnemyMissile : MonoBehaviour {

public float ProjectileSpeed;
public GameObject ExplosionPrefab2;
//public Player Playerscript;
public ScriptName Playerscript;
private Transform myTransform;

void Start () 
{

	myTransform = transform;
}

void Update () 
{
	float amtToMove = ProjectileSpeed * Time.deltaTime;
	myTransform.Translate(Vector3.left * amtToMove);
	
	if (myTransform.position.x < -7f)
		Destroy(gameObject);
}

void OnTriggerEnter(Collider otherObject)
{
	if(otherObject.tag == "Player")
	{
		GameObject[] Player = GameObject.FindGameObjectsWithTag("Player");
		Instantiate (ExplosionPrefab2,  otherObject.transform.position, otherObject.transform.rotation);
		
		
	   	Playerscript = gameObject.GetComponent("Player") as ScriptName;
   		//Playerscript.IEnumerator DestroyShip();
		Playerscript.DestroyShip();
		
		//Destroy(Player[0]);
	}
}

}

First of all your methods are all private (it’s the default visibility). To use a method from outside the own class it has to be public. To start a coroutine you always have to use StartCoroutine.
Keep in mind that when you call StartCoroutine inside your missile script the coroutine runs on that object.

To run the coroutine on the “right” object you can either:

  • Make the DestroyShip method public and call it in the missile script like this:

    Playerscript.StartCoroutine(Playerscript.DestroyShip());

  • Or what would be better create another method that starts the coroutine and make that public:

Inside PlayerScript:

public void DestroyShip()
{
    StartCoroutine(CoDestroyShip);
}

private IEnumerator CoDestroyShip()   // I renamed the coroutine.
{
    [...]

Now you can use the DestroyShip method in your missile script like this:

Playerscript.DestroyShip();

While the answer has already been approved, I would like to add a bit of wisdom on top of it.

A good way to manage a set of possible coroutines from a possibility of multiples outside scripts is as following :

  1. Make sure each outside script can access the gameobject that has the coroutine script. (Use whatever method that works best in your case… be it by linking it through the inspector with a public gameobject variable or by searching for it through the scene either by its name or its tag or both.)

Note : While it’s possible to get it with a static public function, it could also lead to some problem whenever the coroutine tries to access non-static parameters within the coroutine. If you only have static parameters, you can use this method with a static (accessible at all time) function.

  1. Create a public variable which request an integer variable.

     	public void BeginPublicCorouting(int step){
     		StartCoroutine ("CoroutineName", step);
     	}
    
  2. Add the coroutine with a switch variable.

    IEnumerator CoroutineName(int step)
    {
    switch(step){
    default :
    //Whatever corouting function you want by default
    break;
    case 1 :
    //Coroutine 1
    break;
    case 2 :
    //Coroutine 2
    break;
    }

With this, you can call 1 single coroutine name through a public function while accessing multiple possible coroutines simply by calling the BeginPublicCorouting(#); function (replace # by the number of the coroutine you wish to launch).

This is extremely useful when you wish to launch multiple actions in rows which includes some WaitForSeconds variables such as event where the player is not in controls, but still has to confirm some option or UI or whatever during specific part of the process. (For example, the introduction where, at some point, the player has to select his name or whatever option he or she might have while the whole “event” includes characters moving around and stuff.)