211 lines
10 KiB
C#
211 lines
10 KiB
C#
using UnityEngine;
|
|
|
|
namespace MSCO
|
|
{
|
|
/// <summary>
|
|
/// Gemeinsamer Stil aller MSCO-Fenster (Launcher, F7-Konsole, Admin-Menü, Statusleiste):
|
|
/// dunkle Flächen mit abgerundeten Ecken, orange Akzente, einheitliche Schriften und Abstände.
|
|
/// Unitys OnGUI kennt keine runden Ecken - dafür werden kleine Texturen erzeugt und per
|
|
/// 9-Slice (GUIStyle.border) gestreckt, sodass die Ecken rund bleiben.
|
|
/// </summary>
|
|
public static class UiTheme
|
|
{
|
|
public static readonly Color Background = Hex(0x16, 0x16, 0x1a, 0.97f);
|
|
public static readonly Color Panel = Hex(0x0f, 0x0f, 0x12, 0.98f);
|
|
public static readonly Color Row = Hex(0x22, 0x22, 0x28);
|
|
public static readonly Color RowHover = Hex(0x2b, 0x2b, 0x33);
|
|
public static readonly Color FieldColor = Hex(0x22, 0x22, 0x28);
|
|
public static readonly Color FieldFocus = Hex(0x2c, 0x2c, 0x34);
|
|
public static readonly Color Accent = Hex(0xf2, 0x6b, 0x1d);
|
|
public static readonly Color AccentHover = Hex(0xff, 0x82, 0x3a);
|
|
public static readonly Color Secondary = Hex(0x2a, 0x2a, 0x31);
|
|
public static readonly Color SecondaryHover = Hex(0x36, 0x36, 0x3f);
|
|
public static readonly Color Danger = Hex(0xd9, 0x44, 0x48);
|
|
public static readonly Color DangerHover = Hex(0xee, 0x5a, 0x5e);
|
|
public static readonly Color Text = Hex(0xf2, 0xf2, 0xf5);
|
|
public static readonly Color Muted = Hex(0x8e, 0x8e, 0x99);
|
|
public static readonly Color Online = Hex(0x3d, 0xd6, 0x8c);
|
|
public static readonly Color Pending = Hex(0xf2, 0xc2, 0x1d);
|
|
public static readonly Color Gold = Hex(0xff, 0xb4, 0x5a);
|
|
/// <summary>Feiner heller Rand um Fenster und Karten (wie auf der Website).</summary>
|
|
public static readonly Color Border = new Color(1f, 1f, 1f, 0.08f);
|
|
|
|
public const int Radius = 6;
|
|
const int ShadowBlur = 16;
|
|
static GUIStyle shadowStyle;
|
|
|
|
public static Color Hex(int r, int g, int b, float a = 1f)
|
|
{
|
|
return new Color(r / 255f, g / 255f, b / 255f, a);
|
|
}
|
|
|
|
/// <summary>Textur mit abgerundeten Ecken (weiche Kanten) für 9-Slice-Styles.</summary>
|
|
public static Texture2D Rounded(Color color, int radius = Radius)
|
|
{
|
|
int size = radius * 2 + 4;
|
|
var tex = new Texture2D(size, size, TextureFormat.ARGB32, false);
|
|
tex.wrapMode = TextureWrapMode.Clamp;
|
|
tex.filterMode = FilterMode.Bilinear;
|
|
for (int y = 0; y < size; y++)
|
|
{
|
|
for (int x = 0; x < size; x++)
|
|
{
|
|
// Abstand zur nächsten Ecken-Mitte (innerhalb des geraden Teils = 0)
|
|
float cx = Mathf.Clamp(x + 0.5f, radius, size - radius);
|
|
float cy = Mathf.Clamp(y + 0.5f, radius, size - radius);
|
|
float d = Vector2.Distance(new Vector2(x + 0.5f, y + 0.5f), new Vector2(cx, cy));
|
|
float alpha = Mathf.Clamp01(radius - d + 0.5f);
|
|
tex.SetPixel(x, y, new Color(color.r, color.g, color.b, color.a * alpha));
|
|
}
|
|
}
|
|
tex.Apply();
|
|
tex.hideFlags = HideFlags.HideAndDontSave;
|
|
return tex;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Wie Rounded, aber mit senkrechtem Verlauf (oben -> unten) und optional 1 px Rand. Die Textur ist höher als
|
|
/// breit, damit der Verlauf beim 9-Slice-Strecken im Mittelteil erhalten bleibt.
|
|
/// </summary>
|
|
public static Texture2D RoundedEx(Color top, Color bottom, Color border, bool withBorder, int radius = Radius, int height = 48)
|
|
{
|
|
int width = radius * 2 + 4;
|
|
height = Mathf.Max(height, width);
|
|
var tex = new Texture2D(width, height, TextureFormat.ARGB32, false);
|
|
tex.wrapMode = TextureWrapMode.Clamp;
|
|
tex.filterMode = FilterMode.Bilinear;
|
|
for (int y = 0; y < height; y++)
|
|
{
|
|
// Texturzeile 0 ist unten
|
|
Color fill = Color.Lerp(bottom, top, (y + 0.5f) / height);
|
|
// Rand deckend auf die Fläche mischen (ein durchsichtiger Rand ließe das Spiel durchscheinen)
|
|
Color edge = Color.Lerp(fill, new Color(border.r, border.g, border.b, fill.a), border.a);
|
|
for (int x = 0; x < width; x++)
|
|
{
|
|
float cx = Mathf.Clamp(x + 0.5f, radius, width - radius);
|
|
float cy = Mathf.Clamp(y + 0.5f, radius, height - radius);
|
|
float d = Vector2.Distance(new Vector2(x + 0.5f, y + 0.5f), new Vector2(cx, cy));
|
|
Color c = withBorder ? Color.Lerp(edge, fill, Mathf.Clamp01(radius - 1f - d + 0.5f)) : fill;
|
|
c.a *= Mathf.Clamp01(radius - d + 0.5f);
|
|
tex.SetPixel(x, y, c);
|
|
}
|
|
}
|
|
tex.Apply();
|
|
tex.hideFlags = HideFlags.HideAndDontSave;
|
|
return tex;
|
|
}
|
|
|
|
/// <summary>Fenster/Karte mit feinem Rand; mit GUI.Box zeichnen (vorher DrawShadow für Tiefe).</summary>
|
|
public static GUIStyle Card(Color color, int radius = Radius)
|
|
{
|
|
var s = new GUIStyle();
|
|
s.normal.background = RoundedEx(color, color, Border, true, radius, radius * 2 + 4);
|
|
s.border = new RectOffset(radius + 2, radius + 2, radius + 2, radius + 2);
|
|
return s;
|
|
}
|
|
|
|
/// <summary>Orange Hauptknopf mit leichtem Verlauf (wie .btn auf der Website).</summary>
|
|
public static GUIStyle PrimaryButton(int fontSize = 13)
|
|
{
|
|
GUIStyle s = Button(Accent, AccentHover, Color.white, fontSize);
|
|
ApplyPrimaryGradient(s);
|
|
return s;
|
|
}
|
|
|
|
/// <summary>Verlauf auf einen vorhandenen Knopf-Stil legen (Rundung und Rand bleiben).</summary>
|
|
public static void ApplyPrimaryGradient(GUIStyle s)
|
|
{
|
|
s.normal.background = s.focused.background = RoundedEx(AccentHover, Accent, Color.clear, false);
|
|
s.hover.background = s.active.background = s.onNormal.background =
|
|
RoundedEx(Color.Lerp(AccentHover, Color.white, 0.12f), AccentHover, Color.clear, false);
|
|
}
|
|
|
|
/// <summary>Weicher Schatten unter einem Fenster (etwas nach unten versetzt). Vor dem Fenster zeichnen.</summary>
|
|
public static void DrawShadow(Rect r)
|
|
{
|
|
if (shadowStyle == null || shadowStyle.normal.background == null)
|
|
{
|
|
const int radius = 10;
|
|
int reach = radius + ShadowBlur;
|
|
int size = reach * 2 + 4;
|
|
var tex = new Texture2D(size, size, TextureFormat.ARGB32, false);
|
|
tex.wrapMode = TextureWrapMode.Clamp;
|
|
tex.filterMode = FilterMode.Bilinear;
|
|
for (int y = 0; y < size; y++)
|
|
for (int x = 0; x < size; x++)
|
|
{
|
|
float cx = Mathf.Clamp(x + 0.5f, reach, size - reach);
|
|
float cy = Mathf.Clamp(y + 0.5f, reach, size - reach);
|
|
float outside = Mathf.Max(0f, Vector2.Distance(new Vector2(x + 0.5f, y + 0.5f), new Vector2(cx, cy)) - radius);
|
|
float fade = 1f - Mathf.Clamp01(outside / ShadowBlur);
|
|
tex.SetPixel(x, y, new Color(0f, 0f, 0f, 0.45f * fade * fade));
|
|
}
|
|
tex.Apply();
|
|
tex.hideFlags = HideFlags.HideAndDontSave;
|
|
shadowStyle = new GUIStyle();
|
|
shadowStyle.normal.background = tex;
|
|
shadowStyle.border = new RectOffset(reach + 2, reach + 2, reach + 2, reach + 2);
|
|
}
|
|
GUI.Box(new Rect(r.x - ShadowBlur, r.y - ShadowBlur + 6, r.width + ShadowBlur * 2, r.height + ShadowBlur * 2), GUIContent.none, shadowStyle);
|
|
}
|
|
|
|
public static Texture2D Solid(Color color)
|
|
{
|
|
var tex = new Texture2D(1, 1);
|
|
tex.SetPixel(0, 0, color);
|
|
tex.Apply();
|
|
tex.hideFlags = HideFlags.HideAndDontSave;
|
|
return tex;
|
|
}
|
|
|
|
/// <summary>Fläche (Fenster, Karte, Zeile) mit runden Ecken; mit GUI.Box zeichnen.</summary>
|
|
public static GUIStyle Panel9(Color color, int radius = Radius)
|
|
{
|
|
var s = new GUIStyle();
|
|
s.normal.background = Rounded(color, radius);
|
|
s.border = new RectOffset(radius + 2, radius + 2, radius + 2, radius + 2);
|
|
return s;
|
|
}
|
|
|
|
public static GUIStyle Button(Color normal, Color hover, Color text, int fontSize = 13, bool bold = true)
|
|
{
|
|
var s = new GUIStyle(GUI.skin.button);
|
|
s.normal.background = Rounded(normal);
|
|
s.hover.background = Rounded(hover);
|
|
s.active.background = Rounded(hover);
|
|
s.focused.background = Rounded(normal);
|
|
s.onNormal.background = Rounded(hover);
|
|
s.normal.textColor = s.hover.textColor = s.active.textColor = s.focused.textColor = s.onNormal.textColor = text;
|
|
s.border = new RectOffset(Radius + 2, Radius + 2, Radius + 2, Radius + 2);
|
|
s.padding = new RectOffset(12, 12, 6, 6);
|
|
s.fontSize = fontSize;
|
|
s.fontStyle = bold ? FontStyle.Bold : FontStyle.Normal;
|
|
return s;
|
|
}
|
|
|
|
public static GUIStyle Field(int fontSize = 14)
|
|
{
|
|
var s = new GUIStyle(GUI.skin.textField);
|
|
s.normal.background = Rounded(FieldColor);
|
|
s.hover.background = Rounded(FieldFocus);
|
|
s.focused.background = Rounded(FieldFocus);
|
|
s.normal.textColor = s.hover.textColor = s.focused.textColor = Text;
|
|
s.border = new RectOffset(Radius + 2, Radius + 2, Radius + 2, Radius + 2);
|
|
s.padding = new RectOffset(12, 12, 0, 0);
|
|
s.alignment = TextAnchor.MiddleLeft;
|
|
s.fontSize = fontSize;
|
|
return s;
|
|
}
|
|
|
|
public static GUIStyle Label(int fontSize, Color color, FontStyle font = FontStyle.Normal, TextAnchor align = TextAnchor.UpperLeft)
|
|
{
|
|
var s = new GUIStyle(GUI.skin.label);
|
|
s.fontSize = fontSize;
|
|
s.fontStyle = font;
|
|
s.normal.textColor = color;
|
|
s.alignment = align;
|
|
return s;
|
|
}
|
|
}
|
|
}
|