236 lines
7.9 KiB
C#
236 lines
7.9 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
namespace Vanguard.UI
|
|
{
|
|
/// <summary>
|
|
/// Процедурные карточные текстуры без внешних артов: цвет клана,
|
|
/// градиент, рубашка. Позже заменяется Addressables-артом.
|
|
/// </summary>
|
|
public static class CardArt
|
|
{
|
|
private static readonly Dictionary<string, Sprite> Cache =
|
|
new Dictionary<string, Sprite>();
|
|
private static Sprite _back;
|
|
private static Sprite _zone;
|
|
|
|
public static Color ClanColor(string clan) => clan switch
|
|
{
|
|
"RoyalPaladin" => new Color(0.20f, 0.40f, 0.78f),
|
|
"OracleThinkTank" => new Color(0.72f, 0.58f, 0.22f),
|
|
"Kagero" => new Color(0.68f, 0.18f, 0.18f),
|
|
"Nubatama" => new Color(0.38f, 0.22f, 0.62f),
|
|
"Tachikaze" => new Color(0.22f, 0.56f, 0.34f),
|
|
"NovaGrappler" => new Color(0.78f, 0.40f, 0.16f),
|
|
"SpikeBrothers" => new Color(0.52f, 0.34f, 0.20f),
|
|
"DarkIrregulars" => new Color(0.30f, 0.18f, 0.38f),
|
|
"Granblue" => new Color(0.16f, 0.46f, 0.54f),
|
|
"Megacolony" => new Color(0.40f, 0.48f, 0.18f),
|
|
_ => new Color(0.28f, 0.34f, 0.46f),
|
|
};
|
|
|
|
public static Sprite ForClan(string clan)
|
|
{
|
|
var key = "clan:" + clan;
|
|
if (Cache.TryGetValue(key, out var sprite))
|
|
return sprite;
|
|
|
|
var color = ClanColor(clan);
|
|
sprite = Gradient(
|
|
Color.Lerp(color, Color.black, 0.55f),
|
|
Color.Lerp(color, Color.white, 0.18f),
|
|
accent: Color.Lerp(color, Color.white, 0.35f),
|
|
variant: 0);
|
|
Cache[key] = sprite;
|
|
return sprite;
|
|
}
|
|
|
|
public static Sprite ForCard(string nameId, string clan)
|
|
{
|
|
var key = "card:" + nameId;
|
|
if (Cache.TryGetValue(key, out var sprite))
|
|
return sprite;
|
|
|
|
var color = ClanColor(clan);
|
|
var hash = StableHash(nameId ?? string.Empty);
|
|
var accent = Color.HSVToRGB(
|
|
(hash % 360) / 360f,
|
|
0.62f,
|
|
0.98f);
|
|
sprite = Gradient(
|
|
Color.Lerp(color, Color.black, 0.58f),
|
|
Color.Lerp(color, Color.white, 0.16f),
|
|
accent,
|
|
(int)(hash % 5));
|
|
Cache[key] = sprite;
|
|
return sprite;
|
|
}
|
|
|
|
private static uint StableHash(string value)
|
|
{
|
|
var hash = 2166136261u;
|
|
foreach (var ch in value)
|
|
{
|
|
hash ^= ch;
|
|
hash *= 16777619u;
|
|
}
|
|
|
|
return hash;
|
|
}
|
|
|
|
public static Sprite Back
|
|
{
|
|
get
|
|
{
|
|
if (_back != null)
|
|
return _back;
|
|
|
|
var texture = Resources.Load<Texture2D>("CardArt/vanguard_back");
|
|
if (texture != null)
|
|
{
|
|
_back = Sprite.Create(
|
|
texture,
|
|
new Rect(0f, 0f, texture.width, texture.height),
|
|
new Vector2(0.5f, 0.5f),
|
|
100f);
|
|
return _back;
|
|
}
|
|
|
|
_back = BackTexture();
|
|
return _back;
|
|
}
|
|
}
|
|
|
|
public static Sprite Zone
|
|
{
|
|
get
|
|
{
|
|
if (_zone != null)
|
|
return _zone;
|
|
|
|
_zone = ZoneTexture();
|
|
return _zone;
|
|
}
|
|
}
|
|
|
|
private static Sprite ZoneTexture()
|
|
{
|
|
const int size = 64;
|
|
var texture = new Texture2D(size, size, TextureFormat.RGBA32, false)
|
|
{
|
|
filterMode = FilterMode.Bilinear,
|
|
wrapMode = TextureWrapMode.Clamp,
|
|
};
|
|
|
|
var pixels = new Color[size * size];
|
|
var center = (size - 1) * 0.5f;
|
|
for (var y = 0; y < size; y++)
|
|
{
|
|
for (var x = 0; x < size; x++)
|
|
{
|
|
var distance = Mathf.Sqrt((x - center) * (x - center) + (y - center) * (y - center));
|
|
if (distance > 30f)
|
|
{
|
|
pixels[y * size + x] = Color.clear;
|
|
}
|
|
else if (distance > 26f)
|
|
{
|
|
pixels[y * size + x] = new Color(0.38f, 0.52f, 0.78f, 0.9f);
|
|
}
|
|
else
|
|
{
|
|
var glow = Mathf.Clamp01(1f - distance / 26f) * 0.08f;
|
|
pixels[y * size + x] = new Color(
|
|
0.04f + glow,
|
|
0.07f + glow,
|
|
0.12f + glow,
|
|
0.95f);
|
|
}
|
|
}
|
|
}
|
|
|
|
texture.SetPixels(pixels);
|
|
texture.Apply();
|
|
return Sprite.Create(
|
|
texture,
|
|
new Rect(0f, 0f, size, size),
|
|
new Vector2(0.5f, 0.5f),
|
|
100f);
|
|
}
|
|
|
|
private static Sprite Gradient(Color bottom, Color top, Color accent, int variant)
|
|
{
|
|
const int width = 32;
|
|
const int height = 48;
|
|
var texture = new Texture2D(width, height, TextureFormat.RGBA32, false)
|
|
{
|
|
filterMode = FilterMode.Bilinear,
|
|
wrapMode = TextureWrapMode.Clamp,
|
|
};
|
|
|
|
var pixels = new Color[width * height];
|
|
for (var y = 0; y < height; y++)
|
|
{
|
|
var t = y / (float)(height - 1);
|
|
var row = Color.Lerp(bottom, top, t);
|
|
for (var x = 0; x < width; x++)
|
|
{
|
|
var stripe = ((x + y + variant * 3) % 11) < 2 ? 0.12f : 0f;
|
|
var glow = Mathf.Clamp01(1f - Mathf.Abs(x - width * 0.5f) / (width * 0.5f)) * 0.12f;
|
|
|
|
var cx = x - width * 0.5f;
|
|
var cy = y - height * 0.38f;
|
|
var radius = 6f + variant;
|
|
var sigil = Mathf.Abs(cx) + Mathf.Abs(cy) < radius ? 0.22f : 0f;
|
|
if (variant == 2 && Mathf.Abs(cx) < 2f && Mathf.Abs(cy) < 9f)
|
|
sigil += 0.18f;
|
|
|
|
pixels[y * width + x] = Color.Lerp(row, accent, Mathf.Clamp01(stripe + glow + sigil));
|
|
}
|
|
}
|
|
|
|
texture.SetPixels(pixels);
|
|
texture.Apply();
|
|
return Sprite.Create(
|
|
texture,
|
|
new Rect(0f, 0f, width, height),
|
|
new Vector2(0.5f, 0.5f),
|
|
100f);
|
|
}
|
|
|
|
private static Sprite BackTexture()
|
|
{
|
|
const int width = 32;
|
|
const int height = 48;
|
|
var texture = new Texture2D(width, height, TextureFormat.RGBA32, false)
|
|
{
|
|
filterMode = FilterMode.Point,
|
|
wrapMode = TextureWrapMode.Clamp,
|
|
};
|
|
|
|
var pixels = new Color[width * height];
|
|
for (var y = 0; y < height; y++)
|
|
{
|
|
for (var x = 0; x < width; x++)
|
|
{
|
|
var border = x == 0 || y == 0 || x == width - 1 || y == height - 1;
|
|
var diamond = (x + y) % 8 == 0 || (x - y + 64) % 8 == 0;
|
|
pixels[y * width + x] = border
|
|
? new Color(0.42f, 0.56f, 0.82f)
|
|
: diamond
|
|
? new Color(0.12f, 0.22f, 0.42f)
|
|
: new Color(0.05f, 0.09f, 0.17f);
|
|
}
|
|
}
|
|
|
|
texture.SetPixels(pixels);
|
|
texture.Apply();
|
|
return Sprite.Create(
|
|
texture,
|
|
new Rect(0f, 0f, width, height),
|
|
new Vector2(0.5f, 0.5f),
|
|
100f);
|
|
}
|
|
}
|
|
}
|