Skip to content

Instantly share code, notes, and snippets.

@JonathanReid
Last active June 17, 2024 07:34
Show Gist options
  • Save JonathanReid/26f33aab33898dc0d151 to your computer and use it in GitHub Desktop.
Save JonathanReid/26f33aab33898dc0d151 to your computer and use it in GitHub Desktop.
SpriteCreator, a runtime atlassing system for Unity Sprites.
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.UI;
/// <summary>
/// SpriteCreator, a runtime atlassing system for Unity Sprites.
/// Tries to not create any unneccessary textures, cleans all references when Flushed.
/// Can use with or without the atlassing, just call AddToAtlas and Build when using atlasses.
/// Full control over which atlases to add images to.
///
/// Usage:
/// SpriteCreator.Instance.AddTextureToAtlas(Atlas, SpriteCreator.Instance.CreateNewTextureFromPath(Path),_renderer);
/// SpriteCreator.Instance.BuildAtlas(Atlas);
/// </summary>
public class SpriteVO : ScriptableObject
{
public Texture2D SourceTexture;
public string TexutreName;
public Image ImageRenderer;
public SpriteRenderer SpriteRenderer;
public Sprite BuiltSprite;
public List<SpriteRenderer> SharedSpriteRenderers = new List<SpriteRenderer>();
public List<Image> SharedImageRenderers = new List<Image>();
public void LinkSprites()
{
if(ImageRenderer != null)
{
ImageRenderer.sprite = BuiltSprite;
}
else
{
SpriteRenderer.sprite = BuiltSprite;
}
foreach(Image image in SharedImageRenderers)
{
image.sprite = BuiltSprite;
}
foreach(SpriteRenderer sr in SharedSpriteRenderers)
{
sr.sprite = BuiltSprite;
}
}
}
public enum SpriteAtlases
{
UI,
Game,
}
public class SpriteCreator : MonoBehaviour {
private const int ATLAS_WIDTH = 2048;
public SpriteAtlases Atlases;
private Dictionary<Texture, Sprite> _textureSpriteMap;
private Dictionary<string, Texture2D> _stringTextureMap;
private List<Texture2D> _atlas;
private List<List<SpriteVO>> _texturesToAtlas;
private int _previousTextureCount;
private bool _isDirty;
private static SpriteCreator _instance;
public static SpriteCreator Instance
{
get
{
if(_instance == null)
{
_instance = FindObjectOfType<SpriteCreator>();
if(_instance == null)
{
GameObject go = new GameObject();
_instance = go.AddComponent<SpriteCreator>();
go.name = "SpriteCreator";
}
}
return _instance;
}
}
private void Awake()
{
_textureSpriteMap = new Dictionary<Texture, Sprite>();
_stringTextureMap = new Dictionary<string, Texture2D>();
_texturesToAtlas = new List<List<SpriteVO>>();
_atlas = new List<Texture2D>();
for(int i = 0; i<System.Enum.GetNames(typeof(SpriteAtlases)).Length; ++i)
{
Texture2D t = new Texture2D(ATLAS_WIDTH,ATLAS_WIDTH);
_atlas.Add(t);
_texturesToAtlas.Add(new List<SpriteVO>());
}
}
public void FlushAtlas(SpriteAtlases atlas)
{
int i = (int)atlas;
int j = 0, k = _texturesToAtlas[i].Count;
for(;j<k;++j)
{
Destroy(_texturesToAtlas[i][j].SourceTexture);
_texturesToAtlas[i][j].SourceTexture = null;
}
}
public void Flush()
{
foreach(KeyValuePair<Texture, Sprite> kvp in _textureSpriteMap)
{
Destroy(kvp.Key);
Destroy(kvp.Value);
}
_textureSpriteMap = null;
int i = 0, l = _texturesToAtlas.Count;
for(;i<l;++i)
{
int j = 0, k = _texturesToAtlas[i].Count;
for(;j<k;++j)
{
Destroy(_texturesToAtlas[i][j].SourceTexture);
_texturesToAtlas[i][j].SourceTexture = null;
}
_texturesToAtlas[i] = null;
}
_texturesToAtlas = null;
foreach(KeyValuePair<string, Texture2D> kvp in _stringTextureMap)
{
Destroy(kvp.Value);
}
_stringTextureMap = null;
i = 0; l = _atlas.Count;
for(;i<l;++i)
{
Destroy(_atlas[i]);
}
_atlas = null;
Resources.UnloadUnusedAssets();
}
public void AddTextureToAtlas(SpriteAtlases atlas, string path, Image renderer)
{
Texture2D t = CreateNewTextureFromPath(path);
if(!IsTextureInAtlas(atlas, t.name))
{
SpriteVO vo = ScriptableObject.CreateInstance<SpriteVO>();
vo.SourceTexture = t;
vo.TexutreName = t.name;
vo.ImageRenderer = renderer;
_texturesToAtlas[(int)atlas].Add(vo);
}
else
{
GetSpriteForTexture(atlas,t.name).SharedImageRenderers.Add(renderer);
}
_isDirty = true;
}
public void AddTextureToAtlas(SpriteAtlases atlas, string path, SpriteRenderer renderer)
{
Texture2D t = CreateNewTextureFromPath(path);
if(!IsTextureInAtlas(atlas, t.name))
{
SpriteVO vo = ScriptableObject.CreateInstance<SpriteVO>();
vo.SourceTexture = t;
vo.TexutreName = t.name;
vo.SpriteRenderer = renderer;
_texturesToAtlas[(int)atlas].Add(vo);
}
else
{
GetSpriteForTexture(atlas,t.name).SharedSpriteRenderers.Add(renderer);
}
_isDirty = true;
}
private bool IsTextureInAtlas(SpriteAtlases atlas, string t)
{
foreach(SpriteVO vo in _texturesToAtlas[(int)atlas])
{
if(vo.TexutreName == t)
{
return true;
}
}
return false;
}
private SpriteVO GetSpriteForTexture(SpriteAtlases atlas, string t)
{
foreach(SpriteVO vo in _texturesToAtlas[(int)atlas])
{
if(vo.TexutreName == t)
{
return vo;
}
}
return null;
}
public void BuildAtlas(SpriteAtlases atlas)
{
if(_isDirty)
{
int atlasIndex = (int)atlas;
Texture2D a = _atlas[atlasIndex];
List<SpriteVO> vos = _texturesToAtlas[atlasIndex];
int i = 0, l = vos.Count;
Texture2D[] textures = new Texture2D[l];
for(;i<l;++i)
{
textures[i] = vos[i].SourceTexture;
}
Rect[] textureRects = a.PackTextures(textures,2,ATLAS_WIDTH,true);
i = 0; l = textureRects.Length;
for(;i<l;++i)
{
Rect r = textureRects[i];
r.x = r.x * a.width;
r.y = r.y * a.height;
r.width = r.width * a.width;
r.height = r.height * a.height;
Sprite sprite = Sprite.Create(a,r,new Vector2(0.5f,0.5f));
sprite.name = vos[i].TexutreName;
vos[i].BuiltSprite = sprite;
vos[i].LinkSprites();
}
textures = null;
a = null;
vos = null;
_isDirty = false;
Resources.UnloadUnusedAssets();
}
}
public Sprite CreateNewSpriteFromTexture(Texture t)
{
if(t == null)
{
return null;
}
else
{
if(!DoesSpriteExist(t))
{
CreateNewSprite(t);
}
return _textureSpriteMap[t];
}
}
public Sprite CreateNewSpriteFromTexture(string path)
{
if(path != "")
{
Texture t = Resources.Load<Texture>(path);
if(!DoesSpriteExist(t))
{
CreateNewSprite(t);
}
return _textureSpriteMap[t];
}
else
{
return null;
}
}
public Texture2D CreateNewTextureFromPath(string path)
{
if(path != null && path != "")
{
if(!DoesTextureExist(path))
{
CreateNewTexture(path);
}
return _stringTextureMap[path];
}
else
{
return null;
}
}
private bool DoesSpriteExist(Texture t)
{
if(_textureSpriteMap == null)
{
_textureSpriteMap = new Dictionary<Texture, Sprite>();
}
return _textureSpriteMap.ContainsKey(t);
}
private void CreateNewSprite(Texture t)
{
Sprite s = Sprite.Create(t as Texture2D,new Rect(0,0,t.width,t.height),new Vector2(0.5f,0.5f));
_textureSpriteMap.Add(t,s);
}
private bool DoesTextureExist(string path)
{
if(_stringTextureMap == null)
{
_stringTextureMap = new Dictionary<string, Texture2D>();
}
return _stringTextureMap.ContainsKey(path);
}
private void CreateNewTexture(string path)
{
Texture2D t = Resources.Load<Texture2D>(path);
_stringTextureMap.Add(path,t);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment