- Home /
Can I use a custom component as a parameter of a IEnumerator?
I'm trying to make a poison/bleed mechanic by getting the GameObject
of the enemy hit then getting my hp helper custom Component
of that GameObject
then pass it to a helper class that would start a Coroutine
to do the poison/bleed.
In that Coroutine
, I passed the hp helper, dmg, and the duration to the Coroutine
. I keep getting a NPE to something that I don't know, it just point to the StartCoroutine and doesn't point out which variable was causing the NPE so I decided to change way I call the IEnumerator. Before I was using something like StartCoroutine(Coroutine(hpHelper, dmg, duration))
then changed to StartCoroutine("Coroutine", params)
where params is object[]
. Using the latter just gave some stupid, pointing to a line that doesn't make sense so I reverted to using StartCoroutine(Coroutine(hpHelper, dmg, duration))
.
I swear that everything is not null. I'm still trying about what is the best way to make things work, I'm open to your suggestion on how can I implement this.
This is the error I'm getting if you're interested.
NullReferenceException
UnityEngine.MonoBehaviour.StartCoroutine (System.Collections.IEnumerator routine) (at <f38c71c86aa64e299d4cea9fb7c715e1>:0)
Here's the code where it all happens.
public void StartDebuff(Skill skill, GameObject target){
EnemyController enemyController = target.GetComponent<EnemyController>();
HealthHelper targetHpHelp = target.GetComponent<HealthHelper>();
enemyController.debuffLst.Add(skill.debuffType);
switch(skill.debuffType){
case Skill.DebuffType.Poison:
StartCoroutine( DamagePerSecondDebuff(targetHpHelp, skill.buffDebuffValue, skill.buffDebuffDuration) );
break;
case Skill.DebuffType.Bleed:
StartCoroutine( DamagePerSecondDebuff(targetHpHelp, skill.buffDebuffValue, skill.buffDebuffDuration) );
break;
case Skill.DebuffType.Burn:
StartCoroutine( DamagePerSecondDebuff(targetHpHelp, skill.buffDebuffValue, skill.buffDebuffDuration) );
break;
}
}
After some suggestion, I realized that I can't use a MonoBehaviour
class for what I want to do. So I did some changes to my code and made everything a Singleton
. CoroutineHelper
class that contains StartDebuff
is now a singleton and my GameManager
is also now a Singleton
.
Now, I call StartDebuff
like this
CoroutineHelper.Instance.StartDebuff(skill, colllidedToArr[i].gameObject);
Then call a real StartCoroutine()
inside CoroutineHelper
like this
GameManager.Instance.StartChildCoroutine( DamagePerSecondDebuff(targetHpHelp, skill.buffDebuffValue, skill.buffDebuffDuration) );
And that function looks like this
public void StartChildCoroutine(IEnumerator coroutineMethod){
try{
StartCoroutine(coroutineMethod);
}catch(Exception e){
Debug.Log("StartChildCoroutine err: "+e);
}
}
And I still get a mysterious NPE.
StartChildCoroutine err: System.NullReferenceException
at (wrapper managed-to-native) UnityEngine.MonoBehaviour.IsObjectMonoBehaviour(UnityEngine.Object)
at UnityEngine.MonoBehaviour.StartCoroutine (System.Collections.IEnumerator routine) [0x00014] in <f38c71c86aa64e299d4cea9fb7c715e1>:0
at GameManager.StartChildCoroutine (System.Collections.IEnumerator coroutineMethod) [0x00002] in PATH\GameManager.cs:40
UnityEngine.Debug:Log(Object)
GameManager:StartChildCoroutine(IEnumerator) (at Assets/Scripts/Utils/GameManager.cs:42)
CoroutineHelper:StartDebuff(Skill, GameObject) (at Assets/Scripts/Utils/CoroutineHelper.cs:40)
AOEController:Start() (at Assets/Scripts/Controllers/Skills/AOEController.cs:35)
also could you run this in editor or do a debug build? This way it will actually pinpoint the exact line of code for you.
I know the actual line of the error. It points to the first StartCoroutine
. When I double click the error, it does point me to the file and the line that caused the error. But just having a line without knowing which exact variable is causing the error is kinda useless. As someone who came from a language (it's not really a new language) with a specialized IDE for developing like android, I find Unity kinda disappointing with it's lack of the features that make developers life easier.
in which class is your "StartDebuff" method defined? Since you directly use StartCoroutine this class has to be a $$anonymous$$onoBehaviour derived class. So do you actually use a valid instance of that class when you try to call this method? Keep in $$anonymous$$d that you can not create $$anonymous$$onoBehaviour derived classes with "new". Components / $$anonymous$$onoBehaviours have to be attached to a gameobject in the scene in order to run coroutines.
Note that swearing that something is not null doesn't really help ^^. Have you actually checked that the parameters you pass are not null? Keep in $$anonymous$$d that GetComponent will return null (or a fake null object when testing inside the editor) when the component does not exist on the given gameobject.
interesting thoughts. Though what i am not sure about: The instance of the object must be somehow valid, otherwise you could not even call StartDebuff
itself or am i missing something?
Answer by rickymanalo · Jul 13, 2020 at 03:13 PM
Finally made it work. The cause was my incorrect implementation of the various Singleton
s I made
Answer by Tripleganger · Jul 09, 2020 at 09:50 AM
Hi! Unfortunately, you can only pass one parameter when dealing with coroutines.
Also, as a general tip, if possible, try to assign your Components instead of .GetComponent; it is more computationally expensive than most people think.
Oh, that sucks. But will an object array be acceptable? I saw a solution like that where you put the variables you want to pass to the IEnumerator
then retrieve those variables using their respective index. I tried it but it still seems to cause an NP$$anonymous$$ I think I get why GetComponent()
would be expensive. Thanks for the info, will try them later.
Yes, you can totally do that! Here's an example.
void YourFunction()
{
string myString = "Hello";
int myInt = 13;
float myFloat = 3.14f;
object[] myObjects = new object[3]{myString, myInt, myFloat};
StartCoroutine($$anonymous$$yCoroutine, myObjects);
}
private IEnumerator $$anonymous$$yCoroutine(myParameters)
{
//Stuff - you'd get myString as myParameters[0], and so on.
yield return null;
}
I hope this helps.
Based on your first suggestion, I tried the one where you put stuff on a object[]
while still doing GetComponent()
inside StartDebuff
. Unity is still throwing NPE and a bunch more mysterious one like Identifier expected
so I ditch it. The next thing I did was to not use GetComponent
inside StartDebuff
and pass the actual values to it. Just to be sure, I added checks if the variables that I would pass is null then log the value it isn't before I call StartDebuff
. What I got are enemy (EnemyController) and enemy (HealthHelper). But for some reason, the line that calls StartDebuff
now throws a NP$$anonymous$$ I'm really confused.
Answer by Slimer37 · Jul 09, 2020 at 12:31 AM
All I can think of is maybe one of the scripts (either the one that's a parameter or the one running the coroutine) was destroyed, hence the null reference. I have no other ideas. A little tip, though: you can use multiple cases for a single block.
switch(skill.debuffType){
case Skill.DebuffType.Poison:
case Skill.DebuffType.Bleed:
case Skill.DebuffType.Burn:
StartCoroutine( DamagePerSecondDebuff(targetHpHelp, skill.buffDebuffValue, skill.buffDebuffDuration) );
break;
// or you could just use an 'if'
I saw something like that but vscode didn't like it for some reason, or my vscode is just being slow to recognize that it was valid. The GameObject
is not going to be destroyed because I only destroy it after the duration of the skill. I call StartDebuff
in the Start()
of the GameObject
. Could that be a reason for the NPE?
switch looks more elegant though lol