0
\$\begingroup\$

I am trying to implement a simple abstraction over SpriteAtlas where I can simply request texture coordinates for a certain BlockType (each BlockType has associated texture with it that is packed inside a SpriteAtlas. Imagine dirt, stone, water etc.). To achieve just that, I've created a simple ScriptableObject class that is given a reference to the SpriteAtlas and in its OnEnable method it tries to extract all of the texture coordinates from the sprite atlas:

    [SerializeField] private SpriteAtlas spriteAtlas;

    private readonly Dictionary<BlockType, Vector2[]> _blockTypeToTextureCoordinates = new Dictionary<BlockType, Vector2[]>();
    private Sprite[] _sprites;

    private void OnEnable()
    {
        LoadAllSpritesFromSpriteAtlas(); // This method works perfectly fine.
        ExtractTextureCoordinatesFromEachSprite(); // This method has a problem in it.
    }

    private void LoadAllSpritesFromSpriteAtlas()
    {
        _sprites = new Sprite[spriteAtlas.spriteCount];
        spriteAtlas.GetSprites(_sprites);
    }

    private void ExtractTextureCoordinatesFromEachSprite()
    {
        foreach (var sprite in _sprites)
        {
            if (!Enum.TryParse(sprite.texture.name, out BlockType blockType))
            {
                Debug.LogError($"'{sprite.texture.name}' could not be converted to a block type.");
                continue;
            }

            // I've been in debugger and sprite is not yet packed.
            _blockTypeToTextureCoordinates[blockType] = sprite.uv;
        }
    }

The problem lies in the last method ExtractTextureCoordinatesFromEachSprite, where when I am trying to get sprite texture coordinates (uv), it always returns {(0, 0), (0, 1), (1, 0), (1, 1)}, which is a full sprite atlas texture, instead of only a small portion of it (some small texture coordinate numbers between 0 and 1). I used debugger and it said that sprite.packed variable is set to false (meaning the sprites are not yet packed at this point in time). However, if I would do exactly the same code in MonoBehaviour class' Awake method, everything would work fine and sprites would be packed when accessed and appropriate UV coordinates would get returned.

However, I'd really love if I could do it inside a ScriptableObject and it really makes no sense that sprites are not initialized at that point since this information could easily be created even before the game is run. Can anyone help me understand why the sprites are not packed when accessed in OnEnable method of ScriptableObject and is there a way to make it work somehow?

\$\endgroup\$
2
  • \$\begingroup\$ Did you try logging some text in OnEnable to glean when it is that OnEnable runs when working in the editor? \$\endgroup\$
    – DMGregory
    Aug 1 at 11:58
  • \$\begingroup\$ Yeah, it is definitely called when I start the game (I checked that). However, I am not sure how I can check EXACTLY when it is being called (I know it is called before MonoBehaviours, but that is it). \$\endgroup\$
    – FICHEKK
    Aug 1 at 12:04
1
\$\begingroup\$

Will switching this to use lazy initialization help?

// No OnEnable method anymore. Instead, when a sprite UV is requested,
// we check whether we've loaded all the sprite texture coordinates,
// and if not, then we load them "just in time".

public Vector2[] GetUVsForBlockType(BlockType blockType) {
    if (_blockTypeToTextureCoordinates.Count == 0) {
        LoadAllSpritesFromSpriteAtlas();
        ExtractTextureCoordinatesFromEachSprite();
    }

    return _blockTypeToTextureCoordinates[blockType];
}
\$\endgroup\$
1
  • \$\begingroup\$ Yes, this will work! Thank you so much for the idea, you da man! \$\endgroup\$
    – FICHEKK
    Aug 1 at 12:33

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.