[SOLVED] C# - Referencing Variables in Scripts with Interfaces

So I’ve built an awesome conversation mechanic for my game, where players can navigate through by clicking buttons to discuss different topics and all that part’s working great. Now, I also want to be able (at some point in the future) to semi-randomly assign quests to different NPCs - so for example, ‘guard’ NPCs might have various quests to slay so-and-so creature or such-and-such many of so-and-so pest, and so on. So when designing my quest system, I decided to make ‘quests-objects’, or empty gameobjects on which to store scripts with all the data necessary for the quest they’re associated with. That part went great too!

Now, though, I’m trying to put the two together. I want to be able to click a conversation option like ‘Do you need any help?’ and check if the NPC I’m speaking to has a quest-object attached. I figured that making a tag for all the quest-objects was the best way, but…then I realized I’m not sure how to check if an object has a component by anything but name or type (and if so, to get a reference to that component). And each of these quest-objects has a different name, and is really just the generic script type.

Anyone able to point me the right way here? In short, I need an easy way to tell if an object has one of several different components, and I don’t want to have to update a list of included components all the time.

I would make sure all of your quests implemented a common interface, then you can just call GetComponent on that interface.

I guess I’m not sure what you mean by ‘interface’?

Okay, great, this may be what I need! So I learned about interfaces, and I made a super basic one with no elements in it and invoked it on a new ‘Quest1’ script (after learning, I decided to eschew the quest-object idea in lieu of simply attaching the quest’s script directly to the NPC in question).

public class Quest1 : MonoBehaviour, IQuest {

    public string[] greeting;

}

I want this ‘greeting’ to just be the lines the NPC will say if they have this quest available, overriding their individual ‘greeting’. It’s an array because it might be multiple ‘lines’ long, and I want to be able to modify it in the editor. So that part’s good.

Next, I went to the dialogue manager I have. Here’s what I put for the function called when we first interact with an NPC capable of dialogue:

    public void AddNewDialogue (string[] lines, string npcName, GameObject partner) {
        ConvPartner = partner;
        dialogueIndex = 0;
        if (ConvPartner.GetComponent<IQuest> () != null) {
            Quest = ConvPartner.GetComponent<IQuest> ();
            dialogueLines = new List<string> (Quest.greeting.Length);
            dialogueLines.AddRange (Quest.greeting);
        } else {
            dialogueLines = new List<string> (lines.Length);
            dialogueLines.AddRange (lines);
        }
        this.npcName = npcName;
        CreateDialogue ();
    }

This function is called by the NPC, and passes its gameobject as the partner, its greeting as the ‘lines’, and the NPC’s name as the npcName. So that part works great. I kinda guessed on how exactly to check for the quest interface, but I got it right - that part works. I made it display ‘I have a quest!’ instead of those two lines in the if statement there (6 and 7), and it did.

But then I replaced that string with those two lines, and I’m doing it wrong somehow. I think I must have missed a step and it’s referencing the interface itself instead of the script with the instance of the interface I’m wanting to look at. What did I leave out?

I’ve been trying everything I can think of for this and not getting anywhere…I thought that perhaps the issue was because the variable I want to reference (greeting, in this case) isn’t defined in the IQuest itself, so I tried changing IQuest to this:

public interface IQuest {
    string[] greeting;
}

But that didn’t work because evidently you can’t define variables in interfaces. I did more digging, sifting through all the google hits, and next I tried this:

public interface IQuest {
    string[] greeting { get; set; }
}

But this told me my Quest1 didn’t have the right stuff. It was:

public class Quest1 : MonoBehaviour, IQuest {

    public string[] greeting;

}

So I changed it to:

public class Quest1 : MonoBehaviour, IQuest {

    public string[] greeting {get; set;}

}

This feels closer! It doesn’t cause any immediate errors. Only problem is, it removes the greeting array from the editor, so I wasn’t able to actually set the greeting. So I did some more digging around, I tried using the serialize thing on it but evidently that doesn’t work when you declare an array like that. So I thought, oh well, I can define it in the script itself, right? So I tried adding this to Quest1:

public class Quest1 : MonoBehaviour, IQuest {

    public string[] greeting {get; set;}

    void Start () {
        greeting [0] = "I have a quest!";
    }

}

But that doesn’t do at all what I was hoping it would do. Instead, it creates an error:

NullReferenceException: Object reference not set to an instance of an object
(wrapper stelemref) object:stelemref (object,intptr,object)

If anyone can please point me the right way that would be excellent!

Almost there.

[SerialiseFeild]
string [] _greeting;

public string[] greeting {
    get {
        return _greeting;
    } 
    set {
        _greeting = value;
    }
}

Although you could probably drop the set method altogether, not sure you need it.

Thank you so much! I had just gotten to:

public string[] greeting { get; set; }

    void Start () {
        greeting = new string[1];
        greeting [0] = "I have a quest!";
    }

Which wasn’t ideal, but SEEMED to be doing what I wanted. Your solution is much better!