Search Unity

Starting with Unity 5.4, we have added new errors that are displayed when calling the Unity API from constructors/field initializers and during deserialization (loading). The purpose of these errors is to make the Unity API usage requirements clear and avoid any side effects incorrect usage might have.

The majority of the Unity API should only be called from the main thread, e.g. from Start/Update/etc on MonoBehaviour. Similarly, only a subset of the Unity API should be called from script constructors/field initializers, like Debug.Log or Mathf. As Unity will invoke constructors when creating an instance of a class during deserialization (load), which might run on a non-main thread.

These requirements were not strictly enforced in versions of Unity prior to 5.4. Which could lead to crashes, race conditions and issues that were hard to diagnose and reproduce.

In Unity 5.4, the new errors will in most cases not throw a managed exception and will not interrupt the execution flow of your scripts. This approach has been taken to reduce the amount of friction caused by upgrading your projects to Unity 5.4. These errors will throw a managed exception in a future release of Unity. We recommend that you fix these errors (if any) in your project when upgrading to 5.4.

The new errors are documented in the “Script Serialization Errors” section on the Script Serialization page in the manual.

Let’s have a look at the new serialization errors in some common scenarios and how to fix them.

Calling Unity API from constructor/field initializers

When Unity creates an instance of your MonoBehaviour/ScriptableObject derived class, it calls the default constructor to create the managed object. When this happens, we are not yet in the main loop and the scene has not been fully loaded yet. Field initializers are also called when calling the default constructor of a managed object. Calling the Unity API from a constructor is considered unsafe for the majority of the Unity API.

Examples:

image01

image00
In these case we will get the the error “Find is not allowed to be called from a MonoBehaviour constructor (or instance field initializer), call in in Awake or Start instead. …”. The fix is to put the call to the Unity API in MonoBehaviour.Start.

Calling Unity API during deserialization (loading)

When Unity loads a scene, it recreates the managed objects from the saved scene and populates them with the saved values (deserializing). In order to create the managed objects, the default constructor for the objects must be called. If a field referencing an object is saved (serialized) and the object default constructor calls the Unity API you will get an error when loading the scene. As in the previous error, we are not yet in the main loop and the scene is not fully loaded. This is considered unsafe for the majority of the Unity API.

Example:

public class SerializationAPICallBehaviour : MonoBehaviour

image02
Here we get the error “Find is not allowed to be called during serialization, call it from Awake or Start instead.” Fixing this issue requires us to refactor the code to make sure that no Unity API calls are made in any constructors for any serialized objects. If it is necessary to call the Unity API for some objects, then this must be done in the main thread from one of the MonoBehaviour callbacks, such as Start, Awake or Update.

Feedback

If you have any comments, questions or general feedback on these errors, you can post them here in this “Script Serialization Errors Feedback” thread on the Unity 5.4 beta forums.

10 评论

订阅评论

评论被关闭。

  1. Thanks for the post. In-depth articles are always appreciated :).

    One warning though: Adding any log message is NOT an unintrusive thing.

    In the best case, they give some helpful hint to few people of a team.

    In the normal case, they just bother all other team members and everyone who can’t do anything against these messages (and making people just ignore all warnings/errors. Also, errors make the “Error Pause” function quite useless).

    In the worst case, they simply crash (or rather freeze) the editor/game when occurring too frequently.

    Suggestion: What about some “Outstanding Problems” window that just lists all static problems found in a scene? It would not add the same problem twice, ideally recognizing “same problem” before the need to capture any stack trace.

  2. I’m very happy to see these errors/warnings finally introduced to core Unity. Obviously, a working Serializer would be many times more awesome – or being allowed to debug the current weak one – but simply having reliable warnings goes a very long way to allowing us to find and fix (or workaround) the problems ourselves.

    I’ve written extensively about the IMHO very bad Serializer in Unity, and all the many critical bugs and problems (including corrupted projects) it causes. With this update, I can finally start reviewing entire codebases looking for places where this stuff is happening accidentally – including all the 3rd party code (I’m assuming it triggers even on code that you don’t have the source for, e.g. pre-built DLL’s from the asset store?)

    Thanks, guys.

  3. Andrew Raphael Lukasik

    六月 6, 2016 5:23 下午

    In 5.4 I started to see these errors in OnValidate when building apk. I managed to get around them by putting everything there inside [code] if( this.gameObject.activeInHierarchy ) { } [/code] statement. Can you explain how is that connected and will OnValidate change or maybe become obsolete?

    1. Lukasz Paczkowski

      六月 6, 2016 6:01 下午

      Without knowing the details of your project, I believe this issue with the serialization errors being reported while building apk/players/asset bundles is a bug that was recently fixed and the fix should be available in one of the next 5.4 releases.

  4. Robert Cummings

    六月 6, 2016 4:01 下午

    Thanks for doing this. I really dislike ambiguous results (in fact I’m not even sure why unity allows execution at all with errors).

    So this is handy. I would also like more where applicable, anything to create less guesswork :)

  5. Very handy. Since development is being done in this area, does this mean one day more API calls be usable in non-main threads? I don’t understand the underlying engine architecture, but it’s bothersome that some functions such as sprite creation must be called in the main thread. Especially because they eat into frame time so much.

    1. Lukasz Paczkowski

      六月 6, 2016 6:12 下午

      I suggest posting ideas about changes to the Unity API on Unity Feedback, so the appropriate teams can respond. https://feedback.unity3d.com/

    2. There is no way we can safely expose the whole engine API threadsafe, without introducing massive performance regressions. We will likely start exposing small newer lower level functionality API’s over time which has specific requires on how it is used. When we do we want to make sure we can enforce safe usage.

      Today the right approach to multithreading game code, is to create your own cached data structures / tightly packed arrays. Operate in jobs on that data and then integrate the data back later on in the frame when the jobs have completed.

      1. Now as you mention it… there wasn’t anything on the Unite Wishlist/Roadmap talk about the “multi-threading jobs and tight arrays with a similar-to-shader declarative syntax but everything written in C#”.

        Is this feature being worked on? Whom do I have to bribe for more info or even alpha access? :D

      2. I understand there is no smart way of working with game object in non main thread. But few small things would make our life much easier.
        1st – things like Application.dataPath, this is static readonly, no problem with exposing it to other threads.
        2nd – objects independent from game object like Texture2D – sometimes we need to work a little bit on those textures and this eat heavily frame time. Allowing moving such things to other thread would be helpful a lot. Now I am using ugly interface where main thread need to convert format when exchanging data with other thread.
        3rd – some smart way of loading stuff from local storage. It also hurts performance and should be done in background.