Hi ! So, I’ve got a bit C# script, but it seems that i’ve got a null variable or something like that. Here’s my code :
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
namespace DialogSystem
{
public class DialogSystem : MonoBehaviour
{
void Awake () {
string Parameter1 = "Minigame Marimo/PanneauA";
bool[] Parameter2 = {true};
string Parameter3 = "none";
object[] Parameters = new object[3] {Parameter1,Parameter2,Parameter3};
StartCoroutine ("StartDialog",Parameters);
}
public IEnumerator StartDialog (object[] Parameters)
{
string TextPath = (string)Parameters[0];
bool[] DialogTests = (bool[])Parameters[1];
string Audiopath = (string)Parameters[0];
Image[] Sprites = {GameObject.Find ("Left Border's Sprite").GetComponent<Image> (),
GameObject.Find ("Left Center's Sprite").GetComponent<Image> (),
GameObject.Find ("Right Center's Sprite").GetComponent<Image> (),
GameObject.Find ("Right Border's Sprite").GetComponent<Image> ()};
// More stuff
I get this error on the initialization of the Sprites variable. However, i have a gameobject named Left Border’s Sprite… Here’s what my scene’s hierarchy looks like :
So, where’s the problem here ?
Do you know the number of Images you are finding? If not you should probably use a List
Try:
int length = 10; //However many images you expect
Image[] Sprites = new Image[length];
Sprites = {GameObject.Find ("Left Border's Sprite").GetComponent<Image> (),
GameObject.Find ("Left Center's Sprite").GetComponent<Image> (),
GameObject.Find ("Right Center's Sprite").GetComponent<Image> (),
GameObject.Find ("Right Border's Sprite").GetComponent<Image> ()};
the error will have a line reference in it, could you give us that? or just the whole error text
Irrelevant to the question, but I couldn’t help myself not to mention this. I noticed you are using StartCoroutine(“string”, params). To avoid that and create your code a bit more readable and easier to use, you can run your coroutine like this:
StartCoroutine ( StartDialog("Minigame Marimo/PanneauA", new bool[]{true}, "none") );
IEnumerator StartDialog(string textPath, bool[] dialogTests, string audioPath)
{
// rest of the code...
}
That way you can avoid using object[ ], which could pose a problem if someone passed incorrect parameter (wrong types or size). Having function call you can see exactly what you need to pass and someone new in the project can understand what the function does just by examining its name and arguments. Also, if you would refactor your function name at any time, it will be correctly renamed and the coroutine will work. But using StartCoroutine( “string” ) will not be properly renamed since MonoDevelop will not recognise it as a function but a string.
1 Like
@Dudledok : I will only use 4 images, but when i try your code MonoDevelop seems to don’t like the ‘{’ symbol, maybe i would have to give a value to Sprites[0] and so on…?
@LeftyRighty : Oh right. Here you go !
NullReferenceException: Object reference not set to an instance of an object
DialogSystem.DialogSystem+c__Iterator0.MoveNext () (at Assets/Prefabs/DialogSystem.cs:23)
UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
DialogSystem.DialogSystem:Awake() (at Assets/Prefabs/DialogSystem.cs:14)
The 14th line is the StartCoroutine function and the 23th is the first ligne of the assignation of Sprites.
@Fajlworks : Thanks, it looks better that way o/
Bump ! Any ideas please ? I’m still stuck with that one…
If you check your line 23, you will notice:
GameObject.Find("Left Border's Sprite").GetComponent<Image>();
My guess is that you try to Find() your game object, but it wasn’t found. Hence you are trying to call GetComponent() on a variable that is null.
Instead using Find(“string”), I would recommend you to create:
public class DialogSystem : MonoBehaviour
{
public string textPath;
public string audioPath;
public Image[] images; //<--- notice the public var
public bool[] dialogTests;
}
That way you can edit all your images and texts via inspector, and the class will be a bit more reusable.
Another improvement could be to use ScriptableObjects where you could save all your dialogue data in a container accessible via Resources or click and drag via inspector.
public class DialogueModel : ScriptableObject
{
public string textPath;
public string audioPath;
public Image[] images;
public bool[] tests;
}
To create Scriptable object in Editor, please refer to this link:
http://wiki.unity3d.com/index.php?title=CreateScriptableObjectAsset
Also don’t forget to replace YourClass with DialogueModel, or however you want to name it.
Then, you can just simply click and drag that object to your DialogSystems variable in inspector.
public class DialogSystem : MonoBehaviour
{
public DialogueModel model;
void Awake()
{
StartCoroutine( StartDialog() ); // notice how we don't need parameters anymore
}
IEnumerator StartDialog()
{
string textPath = model.textPath;
// etc...
}
}
Thanks for your help Fajlworks ! However, I’d like to call StartDialog a great number of times with differents parameters from others scripts… Is that possible ?
[quote=“Stalzak, post:9, topic: 580142, username:Stalzak”]
Thanks for your help Fajlworks ! However, I’d like to call StartDialog a great number of times with differents parameters from others scripts… Is that possible ?
[/quote]Can you be more explicit when you say “different parameters”? Are they of the same type? Maybe just a different number of a parameter? etc.
Yeah, you could create DialogueModel class to hold all your dialogue data you want to present, using ScriptableObjects. But, if you really want to just pass quickly parameters hardcoded into other scripts, you could create something like, if you ever programmed for iOS, UIAlertView:
public class DialogSystem : MonoBehaviour
{
static string prefabPath = "YourPath/Goes/Here"; //in Resources folder
static DialogSystem kInstance = null;
public DialogueModel model;
private Coroutine m_coroutine;
public static DialogSystem Show( string textPath, string audioPath, Image[] images, bool[] dialogTests )
{
DialogueModel model = ScriptableObject.Create<DialogueModel>();
model.textPath = textPath;
model.audioPath = audioPath;
model.images = images;
model.dialogTests = dialogTests;
return Show( model );
}
public static DialogSystem Show( DialogueModel model )
{
Hide();
if (kInstance == null)
{
GameObject prefab = Resources.Load<GameObject>(prefabPath);
if (prefab.GetComponent<DialogSystem>() == null)
{
Debug.LogError("DialogSystem Error: Prefab does not contain DialogSystem!");
return null;
}
GameObject obj = Instantiate(prefab, Vector3.zero, Quaternion.identity) as GameObject;
kInstance = obj.GetComponent<DialogSystem>();
kInstance.model = model;
kInstance.RunDialogLogic();
}
return kInstance;
}
public static void Hide()
{
if (kInstance != null)
{
if (kInstance.m_coroutine != null)
{
StopCoroutine(kInstance.m_coroutine);
}
Destroy(kInstance.gameObject);
kInstance = null;
}
}
public void RunDialogLogic()
{
StartCoroutine( StartDialog() );
}
IEnumerator StartDialog()
{
string textPath = model.textPath;
//rest of code goes here...
}
}
Now all you have to do is:
void Start()
{
Images[] myImages = ...; //obtain somehow
bool[] tests = new bool[]{ true, true, false };
DialogSystem.Show( "myPath", "mySound", myImages, tests );
}
// or wherever you want to remove your dialogue
void OnDestroy()
{
DialogSystem.Hide();
}
This convenience will allow you to have one dialogue at a time, especially if you will call it many times. However, I urge you to use some class container like DialogueModel, since it will allow you store your tests in a logical way and most importantly separate code from content. You don’t want to hard code your content since it’s a bad practice. Any modification would result opening mono develop and making changes in code, instead of finding the model inside Project window and setting values in inspector.
Hope it helps!
@Dudledok : Ack, sorry for not explaining myself enough. Actually the parameters that i wish to change are the string TextPath to get the dialogue’s content, the bool[ ] DialogTests that contains the results of conditional tests about which part of the dialogue should be read or not, and the string AudioPath that does basically the same thing that TextPath to get the voice acting of the dialogue. Sprites[ ] on the other hand is just an array containing parts of the actual interface, so it doesn’t need to change.
@Fajlworks : Wow, thanks a lot for all the explanations. I’ll give it a try !