Passenger/driver sit still in moving cars: seat pose sent relative to the vehicle (backward-compatible), no backward pull; remote cars/traffic extrapolate in physics time and interpolate; UI polish (shadows, borders, gradient buttons, server list hover, fill bars, colored ping)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Nico Meister
2026-09-28 20:52:56 +02:00
co-authored by Claude Opus 5.5
parent 33a415139b
commit ce2fbdfc10
11 changed files with 274 additions and 24 deletions
+91
View File
@@ -26,8 +26,13 @@ namespace MSCO
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)
{
@@ -58,6 +63,92 @@ namespace MSCO
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);