ScriptableObject variable type mismatch, when it's clearly one type.

[CreateAssetMenu]  
public class GameEvent : ScriptableObject
{
    [SerializeField]
    private List<GameEventListener> listeners = new List<GameEventListener>();
    public void Raise()
    {
        for (int i = listeners.Count - 1; i >= 0; i--)
        {
            listeners[i].OnEventRaised();
        }
    }
    public void RegisterListener(GameEventListener listener)
    {
        listeners.Add(listener);
    }

    public void UnregisterListener(GameEventListener listener)
    {
        listeners.Remove(listener);
    }  
}
public class GameEventListener : MonoBehaviour
{
    public GameEvent Event;
    public UnityEvent Response;

    void OnEnable()
    {
        Event.RegisterListener(this);
    }
    void OnDisable()
    {
        Event.UnregisterListener(this);
    }

    public void OnEventRaised()
    {
        Response.Invoke();
    }
}

Why do I keep getting type mismatch, can anyone help me with that?

3 Likes

What you’re having Unity to do is store an instance of a running GameEventListener into the serializable field of a ScriptableObject, which is an asset on disk. This is just as invalid as dragging a scene instance object into a public field on a Prefab on disk: once you stop running, that on-disk scriptable object is now pointing at a dead object.

5 Likes

I took it from this tutorial(33min)
https://www.youtube.com/watch?v=raQ3iHhE_Kk

How am I getting this wrong/

How was it supposed to work then?

Another link to the same code:

The difference is that the tutorial doesn’t serialize that list. The ScriptableObject is an asset that lives in the project files- it is not specific to any given scene, and cannot serialize scene references and maintain them between you pressing play/stop or building the application. It can be used by scene objects, but only for the duration that the scene is actually running, and never in a way that it’ll properly display its registered listeners in the inspector.

By serializing this list, you’re trying to use it in a way that has no logical way of working. Just leave the [SerializeField] off, and it should work just fine, so long as you don’t expect it to maintain the list between sessions.

4 Likes

Incase anyone else has this problem like I did, to expand on what DonLoquacious said, you can’t serialize the fields, and this includes marking it public I believe. So for me instead of not including [SerializeField] I had to make it private readonly.

I’ve been trying to leverage ScriptableObjects to avoid having to roll my own serialized data system, and for the most part they’re working. However I’ve run into a similar issue where property fields that reference GameObjects aren’t persisting (‘Missing’ or ‘Type Mismatch’) if close and re-open the editor despite even setting dirty manually.

If i’m understanding correctly, ScriptableObjects can’t store references to GameObjects in the hierarchy of a scene.

@auhfel , @DonLoquacious , Is the above assertion correct?

As an aside:
It would be awesome if unity would serialize object references as [Scene].[Object] and display a message to the developer indicating that the referenced princess component is in another castle scene.

I have a feeling i’m going to need to create another custom editor, this time one that shows the GameObject field, but extracts a GUID from it and stores the GUID in the ScriptableObject for use with a scene specific lookup table later…

1 Like

For anyone else who followed the same tutorial as above, it lists the references as Type mismatch in the RuntimeSet but still works. If you look at the example project (provided with the video), his example does the same thing.

Sorry for necroing but I find this very interesting and deserving of discussion.

What are the drawbacks of using a ScriptableObject as a runtime storage for non serializable data?

For instance I can assign a transform from the scene to a Transform field inside a SO asset, this works but it does say “Type mismatch” - even though I can click on the field and the correct transform is pinged in the scene hierarchy, so it’s clearly working. Tested it in builds on a couple devices as well and haven’t noticed anything to indicate otherwise.

There’s mentions in this thread of storing/serializing dead pointers here, which seems a valid drawback - from my testing Unity does flush these dead pointers when e.g. restarting editor etc. and I have not gotten any errors or warnings about this (except the Type mismatch indication inside the field itself).

I mean it does seem like an extremely handy way of managing runtime variables but I’m a bit cautious. Anyone got any more knowledge on this?

4 Likes

There are not really any issues when doing that. In a build game the serialized asset data can not be changed permanently anyways. So after restarting the game all the data is back as it was when you shipped the game. Assigning references to objects (UnityEngine.Object derived objects) which are destroyed later essentially always have the same result: When the object with that reference gets serialized, that reference will simply become null.

Just to clarify the Unite Talk of Ryan Hipple that was mentioned above: They used ScriptableObjects for almost every game data. So there never was any need to reference into the scene because all relevant data was stored as assets. So even runtime values / objects were stored as assets in the project. This allowed scenes and other assets to reference each other without any problems. If you have no idea what I just said, I recommend to watch that Unite talk.

5 Likes