18
\$\begingroup\$

I have a spell system I am creating, the principle is as follows:

  • Each spell is an autonomous prefab. It contains a script with some properties (base damage, duration...) that can be modified in the inspector.
  • I have a Spell enum listing all possible spells in the code, which is used in the game logic
  • When I want to cast a spell, I need to be able to get this spell's prefab to instantiate it and read its informations
  • Each actor (be it players or enemies) needs to have a list of possible animations for the spells

The problems with how I am trying to implement are:

  • For listing each actor's animations I could use a Dictionary<Spell, Animation>, but dictionaries aren't supported by the inspector which makes it hard to easily edit multiple actors type.
  • I need some way to easily access a spell prefab from the corresponding enum. Here too I could use a dictionary but I can only reference to prefabs in the inspector, not in code, meaning I wouldn't be able to fill this dictionary

I am looking for a way to easily associate my spells enums to the corresponding prefabs and animations

\$\endgroup\$
  • 1
    \$\begingroup\$ May I suggest changing the title of this question to refer to getting dictionary-like behaviour (mapping key-value pairs) in Unity's inspector? This will help make the question easier to find for other users who have, or are looking for, ideas to solve this kind of problem. Even though this specific case is about spells, the same techniques are more broadly applicable. \$\endgroup\$ – DMGregory May 3 '14 at 8:32
  • \$\begingroup\$ @DM I don't know Unity, but your general reasoning sounds sensible. Be bold and edit; rollbacks are cheap. \$\endgroup\$ – Anko May 3 '14 at 11:36
24
\$\begingroup\$

One quick way to get key-value pairs in Unity's inspector is to define a serializable entry class, and then use an array or List<> of them. eg...

public class SpellAnimationMap : ScriptableObject
{
   [System.Serializable]
   public class SpellAnimationEntry
   {
       public Spell spell;
       public AnimationClip animation;
   }

   public SpellAnimationEntry[] spellAnimations;    
}

Automatically, this will give you a resizeable list in the inspector where you can enter the key and value, without needing to write a custom inspector.

The result looks like this:

auto-generated inspector

(One trick: if the serialized entry class contains a "Name" field, that string will be displayed instead of the bland "Element 0" headings. Useful if you have more complex data you want to be able to navigate efficiently.)

Making this a ScriptableObject allows you to treat it as an Asset shared between entity types/instances that need the same animation set, avoiding overhead of duplicating the list for each. (Other classes tend to be serialized per-instance in Unity). To go this route, you'll need to add a small editor script to let you create instances of these in your Assets folder.


Edit: now it's even easier - you can just add this attribute above your ScriptableObject:

[CreateAssetMenu(fileName = "fileName.asset", menuName = "Some Folder/Menu Label")]

This puts the ScriptableObject into your Create menu, like so: Screenshot showing a customized Create menu in Unity


You can optionally make the array private and serialized so that it still shows up in the inspector, but add a public Dictionary (or private dictionary with a public GetAnimation(Spell spell) method) for clients to consume for more efficient lookups. In its OnEnable() method, the SpellAnimationMap can iterate through its inspector-populated array to build this dictionary once, again sharing the benefit between all client instances. (Note that OnEnable() is also called in the editor when the asset is first created, so be sure to check that your array is non-null before you try to read it)

Finally, you can add as much into this entry datatype as you need. It could include the prefab too, for instance, or any number of other bits of data you want to link to the spell key.

It's also possible to write custom inspectors to populate Dictionary<,> fields directly, but the impression that I get is that it's fussy to get working smoothly.

\$\endgroup\$
  • \$\begingroup\$ Exactly what I happened to do while waiting an answer, so, I'll accept this one \$\endgroup\$ – Malharhak May 3 '14 at 16:26
0
\$\begingroup\$

Read the explanation provided by @DMGregory

To those who like examples:

public class SpellHandler : ScriptableObject
{
    public Spell[] keys;
    public Animation[] values;

    private Dictionary<Spell, Animation> dic;

    void Awake()
    {
        int length = keys.Length;
        this.dic= new Dictionary<Spell, Animation>(length);
        for (int i = 0; i < length; i++)
        {
            this.dic.Add(keys[i], values[i]);
        } 
    }    
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.