cancelling a running coroutine

Hi there,
I’m having heaps of trouble getting this script to work and I though it would have been an easy one!
I wanted to create this self sufficient function that would show a message onscreen then fade off, but if a previous message was on screen it would delete the old one first.
Now I can delete any previous objects fine but their functions are still running so they end up with missing objects if they are run too close together.

Any suggestions?
Is there a way to maybe cancel the functions that are running as well as destroying the object?

var GeneralMessagePrefab : GameObject;//A gerenal message Text prefab
var MessageObj : GameObject;//The Message Prefab Ends up stored in this
var MessageBlend : float;

function GeneralMessage(MessageSays : String){
	if (MessageObj == true) Destroy(MessageObj);//Delete any previous messages
	MessageObj = Instantiate (GeneralMessagePrefab, Vector3(0.5,0.35,0), Quaternion.identity)as GameObject;
	MessageText = MessageObj.GetComponent(GUIText)as GUIText;//Set Message TextComponent
	MessageText.text = MessageSays;
	MessageBlend = 1;//Reset Blend Amount

	yield WaitForSeconds (1);
	while (MessageBlend > 0){//Fade Message out
   		MessageBlend -= Time.deltaTime;
   		MessageText.material.color.a = Mathf.Lerp(0,0.8,MessageBlend);
	yield;	
	}
	Destroy(MessageObj);//Delete Message after fadeout
}

Thanks
Pete

cancelling a coroutine requires that you used StartCoroutine(“stringForFunctionName”) then you can use StopCoroutine. Alternatively you can use the stop all coroutines function.

but if you stop it, it will just cease to exist, so what you would likely want to do in your case is have a variable on the script that starts it, that indicates if it is meant to continue, like var exit : boolean = false
then you add that exit as second condition to the while. when you now set it to true, the while will terminate and destroy will be executed

Thanks Dreamora, but I’m still having a little trouble,
I updated the script to this -

function GeneralMessage(MessageSays : String){
	if (MessageObj){//Delete any previous messages
		MessageExit = true;
		Destroy(MessageObj);		
	}
	MessageExit = false;
	MessageObj = Instantiate (GeneralMessagePrefab, Vector3(0.5,0.35,0), Quaternion.identity)as GameObject;
	MessageText = MessageObj.GetComponent(GUIText)as GUIText;//Set Message TextComponent
	MessageText.text = MessageSays;
	MessageBlend = 1;//Reset Blend Amount
	yield WaitForSeconds (1);
	while (MessageBlend > 0  MessageExit == false){//Fade Message out
   		MessageBlend -= Time.deltaTime;
   		MessageText.material.color.a = Mathf.Lerp(0,0.8,MessageBlend);
	yield;	
	}
	if (MessageObj)Destroy(MessageObj);//Delete Message after fadeout
}

Now visually it works fine but I still get this error - MissingReferenceException: The object of type ‘GUIText’ has been destroyed but you are still trying to access it…

check out the line in which it happens (double click the error line)

will give you an idea whats going wrong.

My assumption is: not this code :slight_smile:

Hmm, I checked that and it seems to be this line

   		MessageText.material.color.a = Mathf.Lerp(0,0.8,MessageBlend);

Which suggests that is is a problem with that while loop, but it still doesn’t make sense to me why, I thought it would have dropped out of the while function due to the new exit boolean.

That and as a matter of facts it can’t have destroyed the object by that code if it accesses this line.

so the question is: do you have some other code that “searches for objects to delete” basically?

No, I was hoping this message script could handle getting rid of existing messages.

Maybe I have to re-think my approach?

I just realized one thing: You are using a field on the object to store the instantiated object.

Do NOT do that :slight_smile:
At least not unless you rewrite the code at the beginning of the function. You can not just remove the MessageObj if it exists, at least not without ensuring that the old coroutine has fully terminated :slight_smile: (and thats ensured by adding a MessageObj = null; to the end of the function after destroy and alter the start to yield until MessageObj == null :slight_smile: )

Sorry but I think I’m still having trouble wrapping my head around this, I thought storing the instance in the MessageObj would have been good because I could test to see whether it was there or not?

I put this into the while - MessageText != null
and now it doesn’t have any error but the fade out doesn’t seem to happen right.

var GeneralMessagePrefab : GameObject;//A gerena message Text prefab
var MessageObj : GameObject;//The Message Prefab Ends up stored in this
//var MessageBlend : float;
var MessageExit : boolean = false;

function Update(){
	if (Input.GetKeyUp ("q")) {//TEST BUTTON
		GeneralMessage("one");
    }
    if (Input.GetKeyUp ("w")) {//TEST BUTTON
		GeneralMessage("two");
    }
}

function GeneralMessage(MessageSays : String){
	if (GameObject.Find("MessagePrefab(Clone)")){//Delete any previous messages
		MessageExit = true;
		MessageObj = null;
		Destroy(GameObject.Find("MessagePrefab(Clone)"));		
	}
	MessageExit = false;
	MessageObj = Instantiate (GeneralMessagePrefab, Vector3(0.5,0.35,0), Quaternion.identity)as GameObject;
	MessageText = MessageObj.GetComponent(GUIText)as GUIText;//Set Message TextComponent
	MessageText.text = MessageSays;
	var MessageBlend : float = 1;//Reset Blend Amount
	yield WaitForSeconds (.5);
	while (MessageBlend > 0  MessageExit == false  MessageText != null){//Fade Message out
		MessageBlend -= Time.deltaTime*2;
   		MessageText.material.color.a = Mathf.Lerp(0,1,MessageBlend);
	yield;
	}
	if (MessageObj)Destroy(MessageObj);//Delete Message after fadeout
}

I was thinking I might break the script up and have the fadeout script as part of the prefab, so when it gets deleted it goes with the prefab.

you don’t seem to understand.

the problem was that the previous coroutine as first thing checked if MessageObj is present and if so destroy it.
Which is the totally wrong thing to do, cause if it is still there, there is a coroutine that still is working with it.

so what it must do as first thing in the coroutine is check if MessageObj is null and if not, wait until it is.

also your code is lacking the MessageObj = null; at the end of the coroutine.

so all in all what I mentioned / meant :slight_smile:

function GeneralMessage(MessageSays : String){ 
   while( MessageObj != null )
     yield null;
  
   ... // old code stuff inbetween

   Destroy(MessageObj);
   MessageObj = null;
}

or explained differently: your coroutine uses the object MessageObj as if it only existed for itself, yet other coroutines potentially use it too causing some major problems.

Oh cool! I see where I was going wrong.
And that would still work with the exit boolean allowing the previous message to be removed when a new one comes along!
Thanks for sticking with me there Dreamora :slight_smile:
That’s great!