Корректно ли писать логику в scriptableObject?

У меня есть префаб корабля, который имеет характеристики и логику. По своей неопытности при создании этого объекта я сделал обычный спаун. Т.е у каждого спаунящегося объекта были cвои характеристики и прочее, что, конечно, грузило систему. Затем я узнал о scriptableObject, но, как понял, логика там не пишется. Там следует прописывать только описание объекта. Но в курсе Р. Сакутина логика пишется в SO. Вопрос: Корректно ли писать логику в SO и будет ли она вообще в таком случае выполняться?


Ответы (3 шт):

Автор решения: Александр Даниловский

По идее, ScriptableObject создан для хранения информации, настроек, конфигурации. Если в классе-наследнике от ScriptableObject будет реализована логика - то она будет работать. Но зачем нагружать класс предназначенный для хранения информации какой-то бизнес-логикой?

При проектировании лучше все-таки писать классы таким образом, что бы их поведение было предсказуемым, ожидаемым. Вы, к примеру, стали бы ожидать, что книга с описанием технических характеристик корабля сама управляла бы кораблем или командовала экипажем? Но, при этом, вполне ожидаемо было бы, если такая ТТХ-книга могла выдавать какие-нибудь расчеты, вычисления, связанные с хранимыми в ней параметрами.

На логику в ScriptableObject каждый может иметь свою точку зрения. Это холиварный вопрос. Но я бы советовал отделить эту логику.

→ Ссылка
Автор решения: Yaroslav

Чаще всего ими пользуются как контейнером данных, набором однотипных профилей с разными настройками, контейнером с массивом таких профилей или константами. Конечно в таких ScriptableObject могут быть методы для получения данных в другом виде, более удобным образом по enum или ключу, получение рандомного из некого массива, сгенерированных данных на основе настроенных параметров и тд.

Он может даже иметь сущность отвечающую за то какие элементы массива разблокированы для использования и какой текущий и сущность сохранения и загрузки этих данных, как например для набора скинов.

Очень удобно использовать его даже без всяких полей, как общую точку доступа вне сцены вместо статических сиглтонов. Если объект не привязан к конкретной сцене, то можно сделать его ScriptableObject, вот и весь принцип.

ScriptableObject конечно не исполняет магические методы Unity: Start, OnEnable, Update и тд. Если он больше чем контейнер данных, кто-то должен его разок пнуть при старте игры, что бы он проинициализировался/загрузил данные и тд. Некая "точка входа".

У Ромы большой опыт, больше чем у тех, к кому можно обратиться с вопросом на формух в интернетах.

→ Ссылка
Автор решения: RT29a

Писать логику в ScriptableObject очень даже корректно. Но нужно учесть, что в отличии от MonoBehaviour, тут не будет событий вроде Awake(), Start() и прочее.

Например можно подойти к коду в ScriptableObject следующим образом:

  1. Описать интерфейс, шаг опциональный но поможет понять идею:

    public interface ISomeSubSystemManager 
    {
        void Init();
        void DoTheWork();
        void ProcessSomething();
    }
    
  2. Сделать базовый SO на основе этого интерфейса:

    using UnityEngine;
    
    // We don't want create 'CreateAssetMenu' items, because its our base class
    public abstract class DamageSystemManager : ScriptableObject, ISomeSubSystemManager 
    {
        public List<Unit> units { get;set; }
    
        public abstract void Init();
        public abstract void DoTheWork();
        public abstract void ProcessSomething();
    
        public abstract void AddUnit(Unit unit);
    }
    
  3. Описываем логику систем(ы):

    using UnityEngine;
    
    [CreateAssetMenu(fileName = "GroundUnitsDamageSystemManager", menuName = "DamageSystemManagers/GroundUnitsDamageSystemManager", order = 1)]
    public class GroundUnitsDamageSystemManager : DamageSystemManager 
    {
        public override void Init()
        {
            // Init something
        }
    
        public override void DoTheWork()
        {
            // Do some work
        }
    
        public override void ProcessSomething()
        {
            // Process Something
        }
    
        public override void AddUnit(Unit unit);
        {
            // Add and Process new units
        }
    }
    
  4. Уже из основного кода можно работать с созданным(и) экземпляром(и) этих SO:

    using UnityEngine;
    
    public class SomeMonoBehaviour : MonoBehaviour
    {
        public DamageSystemManager damageSystemManager;
    
        void Start()
        {
            damageSystemManager.Init();
        }
    
        void Update()
        {
            damageSystemManager.ProcessSomething();
        }
    }
    

Для описанной выше системы, обычные классы могут быть более пригодны, но я хотел описать, на что можно расcчитывать, если юзать SO как контейнер с кодом.

→ Ссылка