Is there a way to use objects in custom fields? [SOLVED]

Announcements, support questions, and discussion for the Dialogue System.
Post Reply
GingerbreadFetus
Posts: 5
Joined: Sat Feb 13, 2021 11:10 pm

Is there a way to use objects in custom fields? [SOLVED]

Post by GingerbreadFetus »

I want to tie rewards to the quests in my game. I was going to do this using item scriptable objects that could contain multiple items that would be added to the player inventory when it's completed.

I've managed to create the field type like below.

Code: Select all

[CustomFieldTypeService.Name("Reward")]
public class RewardField : CustomFieldType
{
    public RewardPack rewardPack;
    public override string Draw(string currentValue, DialogueDatabase dataBase)
    {
        throw new System.NotImplementedException();
    }

    public virtual object Draw()
    {
        return EditorGUILayout.ObjectField(rewardPack, typeof(RewardPack), true);
    }
}
This does create the field, but with two text fields instead of something like a normal object field like when you serialize an object.

All of the fields seem to be string based. I'm wondering if there is some way that I could tie it to objects instead. So I could assign the reward in the editor via the Scriptable Object, and then retrieve it from the quest log when I need to. But I haven't figured out a way to do this yet.

I followed the example in this how-to post to get this far: https://www.pixelcrushers.com/phpbb/vie ... +to+fields

I was thinking that maybe there was some way to do a call a little like this:

Code: Select all

var rewards = DialogueLua.GetQuestField("Quest 1", "Reward Pack");
And then I could go over the rewards, distributing them to the player as needed.
Last edited by GingerbreadFetus on Sun Feb 14, 2021 8:52 pm, edited 1 time in total.
User avatar
Tony Li
Posts: 22152
Joined: Thu Jul 18, 2013 1:27 pm

Re: Is there a way to use objects in custom fields?

Post by Tony Li »

Hi,

You're correct that values in dialogue databases are primitive types (string, int, etc.). This allows you to export and import content between Unity and other applications without tying values to Unity-specific GUIDs. The tradeoff is that managing non-primitive types is more complex. You can find all item ScriptableObjects, make a string array of their names or other value (e.g., internal ID string), and show that in an EditorGUILayout.Popup. The field will store the item's name or internal ID as a string.

Alternatively, what about tying rewards to conversations instead? So when the player turns in a quest, the quest giver's conversation gives the player one or more items. To do that, inspect a dialogue entry node and expand Events. If your inventory is a ScriptableObject asset, use the Scene-Independent Event. If your inventory is a MonoBehaviour, use a Scene Event:

inventorySceneEvent.png
inventorySceneEvent.png (22.94 KiB) Viewed 317 times

In the screenshot above, PlayerInventory is a component on a GameObject in a specific scene (i.e., the scene containing the NPC). The item (Magic Sword) is a ScriptableObject asset.
GingerbreadFetus
Posts: 5
Joined: Sat Feb 13, 2021 11:10 pm

Re: Is there a way to use objects in custom fields?

Post by GingerbreadFetus »

I think I can make that work. I hadn't thought of that!

There may be a few changes that I have to make. In the game I'm working on the player actually acts as the quest giver. Assigning quests to NPCs. But the approach is basically the same.

Maybe you could illuminate me on this error too. My inventory is a scriptable object, and I wanted to use other scriptable objects called reward packs to add object to the player inventory. So when a quest is complete can get multiple items, maybe some gold.

Inventory looks like this (edit: Should have been clear that AddItems is the method I'm calling from the conversation event):

Code: Select all

[CreateAssetMenu(fileName = "Rewards", menuName = "Items/Player Inventory")]
public class PlayerInventory : ScriptableObject
{
    public List<string> itemsPlaceholder;

    public void AddItems(RewardPack rewards)
    {
        Debug.Log("adding rewards...");
        foreach(string reward in rewards.rewardItems)
        {
            itemsPlaceholder.Add(reward);
        }
    }
}
Reward packs are simple for now:

Code: Select all

[CreateAssetMenu(fileName = "Rewards", menuName = "ScriptableObjects/Reward Pack")]
public class RewardPack : ScriptableObject
{
    //TODO eventually I'll make an item SO to replace this...
    public List<string> rewardItems;
    public int gold;
}
I've also attached an image showing how I've set up the event.

But I'm getting this error:

Code: Select all

ArgumentException: Object of type 'UnityEngine.Object' cannot be converted to type 'RewardPack'.
If this doesn't work, what would be the best way to add multiple items to the event arguments?
Attachments
EventSetup.JPG
EventSetup.JPG (17.41 KiB) Viewed 315 times
User avatar
Tony Li
Posts: 22152
Joined: Thu Jul 18, 2013 1:27 pm

Re: Is there a way to use objects in custom fields?

Post by Tony Li »

Hi,

What version of Unity are you using? That code works fine in Unity 2020.1.

Double check that you've selected the correct method.
GingerbreadFetus
Posts: 5
Joined: Sat Feb 13, 2021 11:10 pm

Re: Is there a way to use objects in custom fields?

Post by GingerbreadFetus »

Oh god, I just realized I was getting the error because I accidentally added the event to the start node and it had no argument assigned to it...

After fixing that. This actually works rather well.

For reference, here is the code I'm using to resolve the quests that the problem was coming from:

Code: Select all

private void ResolveQuest(Adventurer adventurer)
    {
        //TODO: There are a lot of cases here to cover still
        //Did the adventurer succeed?
        //TODO Defaulting to all being successful for now. Placeholder for method to calculate success later.
        bool questSuccess = true;
        if (questSuccess)
        {
            QuestLog.SetQuestState(adventurer.AssignedQuest, QuestState.Success); 
        }
        else
        {
            QuestLog.SetQuestState(adventurer.AssignedQuest, QuestState.Failure);
        }

        //Start resolution conversation.
        DialogueManager.StartConversation("Resolve " + adventurer.AssignedQuest);
        
        //Reset adventurer state
        adventurer.adventurerState = AdventurerState.Available;
        adventurer.AssignedQuest = null;
        adventurer.gameObject.SetActive(true);

        //TODO if the quest has an isRepeatable field, set it back to unassigned.
    }
This does mean that I have to have a resolution conversation for each quest however. Though I think I can get around that by instead making a database of reward packs that I can reference with the active quest name and get out the desired rewards. Or something like that.

And, using larger conversations that branch depending on what quest they just finished.

This was a big help, thank you! (I'm sure I'll be back sometime later too.)
User avatar
Tony Li
Posts: 22152
Joined: Thu Jul 18, 2013 1:27 pm

Re: Is there a way to use objects in custom fields? [SOLVED]

Post by Tony Li »

Happy to help!
Post Reply