Friday, August 24, 2018

architecture - How to edit key-value pairs (like a Dictionary) in Unity's inspector?



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, 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



Answer



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.


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...