using System.Collections.Generic; using UnityEngine; namespace Ashfall.Runtime { public sealed class SpriteLibrary : System.IDisposable { private readonly Dictionary sprites = new Dictionary(); public Material Lit { get; } public Material Unlit { get; } public SpriteLibrary() { var lit = Shader.Find("Universal Render Pipeline/2D/Sprite-Lit-Default"); var unlit = Shader.Find("Universal Render Pipeline/2D/Sprite-Unlit-Default"); Lit = new Material(lit != null ? lit : Shader.Find("Sprites/Default")); Unlit = new Material(unlit != null ? unlit : Shader.Find("Sprites/Default")); AddAtlas("ExpeditionAtlas", "human", 0, true); AddAtlas("ExpeditionAtlas", "adult", 1, true); AddAtlas("ExpeditionAtlas", "crawler", 2, false); AddAtlas("ExpeditionAtlas", "prop", 3, false); AddAtlas("LifeformsAtlas", "worm", 0, false); AddAtlas("LifeformsAtlas", "beast", 1, false); AddAtlas("LifeformsAtlas", "ark", 2, false); AddAtlas("LifeformsAtlas", "extra", 3, false); sprites["white"] = MakeTile("white", false, false); sprites["soil"] = MakeTile("soil", true, false); sprites["metal"] = MakeTile("metal", true, true); sprites["shadow"] = MakeShadow(); } public Sprite Get(string key) => sprites.TryGetValue(key, out var sprite) ? sprite : sprites["white"]; public void Dispose() { foreach (var pair in sprites) { if (pair.Key == "white" || pair.Key == "soil" || pair.Key == "metal" || pair.Key == "shadow") Object.Destroy(pair.Value.texture); Object.Destroy(pair.Value); } sprites.Clear(); Object.Destroy(Lit); Object.Destroy(Unlit); } public Sprite Direction(string name, float heading) { int column = Mathf.RoundToInt(Mathf.Repeat(-heading, 360) / 90) % 4; return Get(name + column); } private void AddAtlas(string file, string name, int row, bool feet) { var texture = Resources.Load("Art/" + file); if (texture == null) return; float cellW = texture.width / 4f, cellH = texture.height / 4f; for (int column = 0; column < 4; column++) sprites[name + column] = Sprite.Create(texture, new Rect(column * cellW, (3 - row) * cellH, cellW, cellH), new Vector2(.5f, feet ? .12f : .5f), cellW, 0, SpriteMeshType.FullRect); } private static Sprite MakeTile(string name, bool noise, bool seams) { const int n = 64; var tex = new Texture2D(n, n, TextureFormat.RGBA32, false) { name = "Procedural " + name, filterMode = FilterMode.Bilinear, wrapMode = TextureWrapMode.Repeat }; var pixels = new Color[n * n]; for (int y = 0; y < n; y++) for (int x = 0; x < n; x++) { uint h = unchecked((uint)(x * 374761393 + y * 668265263 + 71)); h = (h ^ (h >> 13)) * 1274126177; float v = noise ? .74f + (h & 255) / 255f * .22f : 1; if (seams && (x < 2 || y < 2)) v *= .48f; if (seams && x > 4 && x < 8 && y > 4 && y < 8) v *= .7f; pixels[y * n + x] = new Color(v, v, v, 1); } tex.SetPixels(pixels); tex.Apply(); return Sprite.Create(tex, new Rect(0, 0, n, n), new Vector2(.5f, .5f), n); } private static Sprite MakeShadow() { const int n = 64; var tex = new Texture2D(n, n, TextureFormat.RGBA32, false) { name = "Contact shadow", filterMode = FilterMode.Bilinear }; var pixels = new Color[n * n]; for (int y = 0; y < n; y++) for (int x = 0; x < n; x++) { float d = Vector2.Distance(new Vector2(x, y), new Vector2(31.5f, 31.5f)) / 31.5f; pixels[y * n + x] = new Color(0, 0, 0, Mathf.Pow(Mathf.Clamp01(1 - d), .7f) * .7f); } tex.SetPixels(pixels); tex.Apply(); return Sprite.Create(tex, new Rect(0, 0, n, n), new Vector2(.5f, .5f), n); } } }