📜 ⬆️ ⬇️

Unite Europe 2016: Review of the report “Overthrowing the MonoBehavior your tyranny in a glorious ScriptableObject revolution”



We recently visited Amsterdam at the Unite Europe 2016 conference, where we received a lot of emotions and interesting experience. At this conference there were a lot of fascinating reports in different directions and different levels of complexity. The theme of one of the speeches was “Overthrowing the MonoBehavior tyranny in a glorious ScriptableObject revolution”, in which Richard Fine ( https://twitter.com/superpig / https://github.com/richard-fine ), a specialist from Unity Technologies, He spoke in detail about ScriptableObject and showed by examples how it can be applied in the project.

In his report, Richard raised the following questions:


Next is a free translation / retelling of what Richard was talking about, with various additions.
')

Tyranny MB


General information about MB:


What are the disadvantages?


How can you escape the tyranny of MB?


Pure static C # classes?


But we specifically use the engine, which provides all this "out of the box" to avoid inconvenience!

What about prefabs ?
They solve the problem of storage and transfer from scene to scene and between projects, and do not violate VCS granulation. But this solution also has disadvantages:


SO comes to the rescue


SO is a class that allows you to store a large amount of information transmitted regardless of the script samples. This class can be inherited if there is a need to create objects that will not be attached to the GO.

Imagine that there is a prefab with a script that has an array of a million integers. The array occupies 4 megabytes of memory and belongs to the prefab. Each time, creating an instance of this prefab, an instance of this array is also created. If you create 10 game objects, then the size of the memory occupied by the arrays for these 10 instances will be equal to 40 megabytes.

When using SO, the result will be completely different. Unity serializes all types of primitives, strings, arrays, lists, specific types such as Vector3, and custom classes with the Serializable attribute as copies relating to the object in which they are defined. This means that when creating an instance of a class SO with an array of one million integers declared in it, this array will be passed along with the sample. In this case, the instances believe that they have different data. SO fields or any UnityEngine.Object fields, such as MonoBehaviour, Mesh, GameObject, etc., are stored in links, as opposed to values. If there is a script that references SO, containing a million integers, then Unity will save in the script data only a reference to SO. turn SO stores the array. 10 copies of the prefab that refer to the SO class using 4 megabytes of memory would end up occupying 4 megabytes instead of 40 mentioned above. This is especially important when it comes to a large number of objects and / or large amounts of data in scripts.

So SO:


How SO saves us from problems:


But SO is also not perfect:


How to use SO


The SO class should be used when it is necessary to reduce the memory consumption by avoiding copying values. But it can also be used to determine which data sets to include. It is great for configuration files, such as level settings, global game settings, or individual settings for characters / enemies / buildings, and so on (for example, you can store maximum health, damage, and other parameters). SO is also handy when writing custom tools, level editors, etc.

From SO instances, you can quickly and conveniently create custom assets, reuse them, load them over the network, etc. When you declare a successor to the SO class, you can mark it with the CreateAssetMenu attribute, which adds an asset creation point for this object to the context menu of Assets / Create.

An example of a simple script with settings:

using UnityEngine; [CreateAssetMenu(fileName="EnemyData", menuName="Prefs/Characters/Enemy", order=1)] public class EnemyPrefs : ScriptableObject { public string objectName = "Enemy"; [Range(10f, 100f)] public float maxHP = 50f; [Range(1f, 10f)] public float maxDamage = 5f; public Vector3[] spawnPoints; } 

Creating an asset:



Serialization of an asset with the data in the inspector:



When working in the inspector with SO instances, you can double-click on the reference field to open the inspector for your SO. It is also possible to create a custom editor to define the type of inspector of its type to help manage the data it represents.

An SO instance can be created without binding to an .asset file: programmatically, using SriptableObject.CreateInstance <> .

SO lifetime:


Patterns of use


Most developers consider SO as a data container, but in reality it is something more. Consider some of the patterns of its application.

As data objects and tables:


Example:

 class EnemyInfo : ScriptableObject { public int MaximumHealth; public int DamagePerMeleeHit; } class Enemy : MonoBehaviour { public EnemyInfo info; } 

As expandable listings:


Example:

 class AmmoType : ScriptableObject { } … if (inventory[weapon.ammoType] == 0) { PlayOutOfAmmoSound(); return; } … inventory[weapon.ammoType] -= 1; ... 

Dual serialization

As mentioned earlier, one of the advantages of SO is full compatibility with the Unity serialization system. Wherein:


As singletons:


Example:

 class GameState: ScriptableObject { public int lives, score; static GameState _instance; public GameState Instance { get { if (!_instance) _instance = FindObjectOfType<GameState>(); if (!_instance) _instance = CreateDefaultGameState(); return _instance; } } } 

As delegates:


Example:

 abstaract class PowerupEffect : ScriptableObject { public abstract void ApplyTo(GameObject go); } class HealthBooster : PowerupEffect { public int Amount; public override void ApplyTo(GameObject go) { go.GetComponent<Health>().currentValue += Amount; } } class Powerup : MonoBehaviour { public PowerupEffect effect; public void OnTriggerEnter(Collider other) { effect.ApplyTo(other.gameObject); } } 

Thus, Powerup MB delegates its work to PowerupEffect SO, which is the pattern.

Conclusion


Summarizing the above, we can conclude that SO has its pros and cons, some of which are given below:

Benefits:


Disadvantages:


SO is a very important tool that, although it cannot completely replace MB, it can be used in conjunction with it, eliminating its tyranny. SO should not be used for everything, but it gives you more flexibility. Much more than those who are familiar with SO superficially or not at all. It also provides an opportunity to build an excellent workflow for your project, create convenient and useful tools and templates for designers, etc.

Richard at the beginning of his speech stated: “MonoBehavior sucks” (MonoBehaviour sucks :)). It is difficult to completely agree with this, because one way or another, one can hardly do without it (MB). But the main thing is to understand that you should not use it always and everywhere, and that there are various alternatives, one of which is the powerful, flexible and convenient ScriptableObject. It is necessary to correctly choose one or other means, based on the task under specific conditions.

Source: https://habr.com/ru/post/306514/


All Articles