Door/hood sync via hinge spring instead of teleport (fixes NaN crash and parts falling off); join/leave toasts
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MSCO
|
||||
{
|
||||
/// <summary>
|
||||
/// Kleine Hinweise oben rechts (unter der Ping-Anzeige), z.B. "Bob joined" / "Bob left".
|
||||
/// Gleiten von rechts herein, bleiben ein paar Sekunden und blenden dann aus. Höchstens 4 gleichzeitig.
|
||||
/// </summary>
|
||||
public class Toasts
|
||||
{
|
||||
const float Lifetime = 4.5f;
|
||||
const float SlideIn = 0.25f;
|
||||
const float FadeOut = 0.6f;
|
||||
const float Width = 250f;
|
||||
const float Height = 36f;
|
||||
const int MaxShown = 4;
|
||||
|
||||
class Toast
|
||||
{
|
||||
public string Text;
|
||||
public string Detail;
|
||||
public Color Dot;
|
||||
public float Start;
|
||||
}
|
||||
|
||||
readonly List<Toast> toasts = new List<Toast>();
|
||||
GUIStyle box, title, detail;
|
||||
Texture2D dotTex;
|
||||
|
||||
public void Add(string text, string detail, Color dot)
|
||||
{
|
||||
toasts.Add(new Toast { Text = text, Detail = detail, Dot = dot, Start = Time.realtimeSinceStartup });
|
||||
if (toasts.Count > MaxShown) toasts.RemoveAt(0);
|
||||
}
|
||||
|
||||
public void Draw()
|
||||
{
|
||||
if (toasts.Count == 0) return;
|
||||
EnsureStyles();
|
||||
float now = Time.realtimeSinceStartup;
|
||||
toasts.RemoveAll(t => now - t.Start > Lifetime);
|
||||
|
||||
Color before = GUI.color;
|
||||
float y = 40f; // unter der Ping-Anzeige
|
||||
for (int i = toasts.Count - 1; i >= 0; i--) // neueste oben
|
||||
{
|
||||
Toast t = toasts[i];
|
||||
float age = now - t.Start;
|
||||
float slide = Mathf.Clamp01(age / SlideIn);
|
||||
slide = 1f - (1f - slide) * (1f - slide); // weich abbremsen
|
||||
float alpha = Mathf.Clamp01((Lifetime - age) / FadeOut);
|
||||
|
||||
var r = new Rect(Screen.width - 12 - Width + (1f - slide) * (Width + 20f), y, Width, Height);
|
||||
GUI.color = new Color(1f, 1f, 1f, alpha);
|
||||
GUI.Box(r, GUIContent.none, box);
|
||||
|
||||
GUI.color = new Color(t.Dot.r, t.Dot.g, t.Dot.b, alpha);
|
||||
GUI.DrawTexture(new Rect(r.x + 14, r.y + Height / 2f - 4, 8, 8), dotTex);
|
||||
|
||||
GUI.color = new Color(1f, 1f, 1f, alpha);
|
||||
GUI.Label(new Rect(r.x + 32, r.y, r.width - 40, Height), t.Text, title);
|
||||
if (!string.IsNullOrEmpty(t.Detail))
|
||||
{
|
||||
float w = title.CalcSize(new GUIContent(t.Text)).x;
|
||||
GUI.Label(new Rect(r.x + 32 + w + 6, r.y, r.width - 46 - w, Height), t.Detail, detail);
|
||||
}
|
||||
y += Height + 6f;
|
||||
}
|
||||
GUI.color = before;
|
||||
}
|
||||
|
||||
void EnsureStyles()
|
||||
{
|
||||
if (box != null && box.normal.background != null) return;
|
||||
box = UiTheme.Panel9(new Color(0.06f, 0.06f, 0.07f, 0.88f), 6);
|
||||
title = new GUIStyle(GUI.skin.label) { fontSize = 13, fontStyle = FontStyle.Bold, alignment = TextAnchor.MiddleLeft };
|
||||
title.normal.textColor = Color.white;
|
||||
title.wordWrap = false;
|
||||
detail = new GUIStyle(title) { fontStyle = FontStyle.Normal, fontSize = 12 };
|
||||
detail.normal.textColor = UiTheme.Muted;
|
||||
dotTex = RoundDot(16);
|
||||
}
|
||||
|
||||
static Texture2D RoundDot(int size)
|
||||
{
|
||||
var t = new Texture2D(size, size, TextureFormat.ARGB32, false);
|
||||
float c = (size - 1) / 2f;
|
||||
for (int x = 0; x < size; x++)
|
||||
for (int y = 0; y < size; y++)
|
||||
{
|
||||
float d = Mathf.Sqrt((x - c) * (x - c) + (y - c) * (y - c));
|
||||
t.SetPixel(x, y, new Color(1f, 1f, 1f, Mathf.Clamp01(c + 0.5f - d)));
|
||||
}
|
||||
t.Apply();
|
||||
t.hideFlags = HideFlags.HideAndDontSave;
|
||||
return t;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user