0
\$\begingroup\$

I adjust my level settings in a Scriptable Object and I want to access that Scriptable Object from a non-Monobehaviour C# class. When I use ScriptableObject.CreateInstance<> method, I get default values of fields in ScriptableObject; but I want to access the values that I adjusted in the inspector. How can I do that ?

My scriptable object class

[CreateAssetMenu(fileName = "NewLevelSetting", menuName = "Level/New Level Setting")]
public class ScriptableLevelSettings : ScriptableObject
{
    // Only for initial level settings

    [SerializeField] private int _forwardPlatformCount = 30;
    public int ForwardPlatformCount { get => _forwardPlatformCount; }

    [SerializeField] private int _bendPlatformCount = 0;
    public int BendPlatformCount { get => _bendPlatformCount; }

    [SerializeField] private int _forwardPlatformIncreaseAmount = 2;
    public int ForwardPlatformIncreaseAmount { get => _forwardPlatformIncreaseAmount; }

    [SerializeField] private int _bendPlatformIncreaseAmount = 1;
    public int BendPlatformIncreaseAmount { get => _bendPlatformIncreaseAmount; }
}

LevelSettings class should access the scriptableobject to set its initial values and adjust those values with the given "level".

public class LevelSettings
{
    private ScriptableLevelSettings _initialSettings;

    private int _forwardPlatformCount = 30;
    public int ForwardPlatformCount { get => _forwardPlatformCount; private set => _forwardPlatformCount = value; }

    private int _bendPlatformCount = 0;
    public int BendPlatformCount { get => _bendPlatformCount; private set => _bendPlatformCount = value; }

    private int _forwardPlatformIncreaseAmount = 2;

    private int _bendPlatformIncreaseAmount = 1;

    private int _maxBendPlatformCount = 3;


    public LevelSettings(int level)
    {
        InitializeSettings();
        SetSettings(level);
    }

    private void InitializeSettings()
    {
        _initialSettings = ScriptableObject.CreateInstance<ScriptableLevelSettings>();
        if (_initialSettings != null)
        {
            _forwardPlatformCount = _initialSettings.ForwardPlatformCount;
            _bendPlatformCount = _initialSettings.BendPlatformCount;
            _forwardPlatformIncreaseAmount = _initialSettings.ForwardPlatformIncreaseAmount;
            _bendPlatformIncreaseAmount = _initialSettings.BendPlatformIncreaseAmount;
        }
    }

    private void SetSettings(int level)
    {
        _forwardPlatformCount += level * _forwardPlatformIncreaseAmount;
        // Uncomment for linear bend count increase
        //_bendPlatformCount += Mathf.FloorToInt(level / 15) * _bendPlatformIncreaseAmount;
        _bendPlatformCount += Random.Range(0, _maxBendPlatformCount);
    }
}

I want my LevelSettings class to access the values below:

enter image description here

\$\endgroup\$
1
\$\begingroup\$

Myself, I'd invert the dependency here:

  • Rather than the current level settings class being responsible for searching the ether to find the particular settings asset it should be using...

  • ...the level settings asset can be responsible for producing current level settings as a service, like a factory.

This can also make the current level settings very lightweight - a "Plain Old Data Structure". We could even implement it as an immutable struct:

[System.Serializable]
public struct LevelSettings {
    [SerializeField] int _forwardPlatformCount;
    public int ForwardPlatformCount { get { return _forwardPlatformCount; } }

    [SerializeField] int _bendPlatformCount;
    public int BendPlatformCount { get { return _bendPlatformCount; } }

    public LevelSettings(int forwardPlatformCount, int bendPlatformCount) {
        _forwardPlatformCount = forwardPlatformCount;
        _bendPlatformCount = bendPlatformCount;
    }
}

Then the work of generating a set of level settings according to a particular progression rule is the responsibility of a "Level Generation Settings" asset:

[CreateAssetMenu(fileName ="LevelGenerationSettings.asset", menuName ="Settings/Level Generation")]
public class LevelGenerationSettings : ScriptableObject {
    [SerializeField] LevelSettings _initialSettings = new LevelSettings(30, 0);

    [SerializeField] int _forwardPlatformIncreaseAmount;
    [SerializeField] int _bendPlatformIncreaseAmount;
    [SerializeField] int _maxBendPlatforms;

    public LevelSettings GetInitialSettings() {
        return _initialSettings;
    }

    public LevelSettings AdvanceLevel(LevelSettings current, int steps) {
        int bends = current.BendPlatformCount;
        for (int i = 0; i < steps; i++)
            bends += _bendPlatformIncreaseAmount + Random.Range(0, _maxBendPlatforms);

        int forwards = current.ForwardPlatformCount
                    + _forwardPlatformIncreaseAmount * steps;

        return new LevelSettings(forwards, bends);
    }
}

Your LevelGenerator script has knowledge about what kinds of levels it's supposed to be generating. It can hold a reference to the LevelGenerationSettings asset it should be using - possibly passed to it from a difficulty/mode selection script, or as part of the inspector configuration in this mode's scene file, like:

[SerializeField] LevelGenerationSettings _generationSettings;

(This also lets you swap out different settings asset files for experimental testing, even if you only use one canonical settings asset in the released game)

Then the generator can produce the current level settings like:

_levelSettings = _generationSettings.GetInitialSettings();

And advance to the next level like:

_levelSettings = _generationSettings.AdvanceLevel(_levelSettings, 1);
\$\endgroup\$
1
1
\$\begingroup\$

Yeah, what you trying to do won't work; ScriptableObjects by definition are never instantiated in-game (instead they are "instantiated" in-project.)

ScriptableObjects can have methods but as such they really function more like static classes.

So, to get a reference to them you'll need to refer to them either as an exposed variable populated in the inspector in a Monobehavior, or by loading it/them at runtime as a Resource or Addressable or AssetBundle.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.