Last active
April 9, 2025 07:27
-
-
Save JonathanReid/26f33aab33898dc0d151 to your computer and use it in GitHub Desktop.
SpriteCreator, a runtime atlassing system for Unity Sprites.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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