Crisp launcher (sizes and fonts scaled in screen pixels instead of a GUI matrix), fps command and display, fewer hitches (staggered scans, shared rigidbody scan, crash-damage mesh reads only after impacts, rarer vehicle fx lookups, cached display prefs)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-01 20:26:43 +02:00
co-authored by Claude Opus 5.5
parent 0b55d270e5
commit 7d341126e8
13 changed files with 381 additions and 231 deletions
+34 -8
View File
@@ -14,8 +14,10 @@ namespace MSCO
public class DamageSync
{
const float ScanInterval = 10f;
const float CheckInterval = 1f; // Fahrzeuge, die wir fahren
const float FreeCheckInterval = 5f; // geparkte Fahrzeuge (Unfall nur durch Anstoßen) seltener
const float CheckInterval = 0.5f; // so oft nach einem Aufprall prüfen
const float ImpactWindow = 4f; // so lange nach einem Aufprall prüfen (Verformung läuft ein paar Frames)
const float ImpactDeltaV = 2.5f; // plötzliche Geschwindigkeitsänderung (m/s pro Frame) = Aufprall
const float SafetyCheckInterval = 30f; // sonst nur selten (z.B. Reparatur in der Werkstatt)
const float MinMove = 0.002f;
const int MaxPerMessage = 4000;
@@ -26,13 +28,18 @@ namespace MSCO
public int Key;
public string Root;
public MeshFilter Filter;
public Rigidbody Body; // Fahrzeug, zu dem das Mesh gehört
public Vector3[] Known; // zuletzt gesendet/empfangen
}
readonly Dictionary<int, DamageMesh> meshes = new Dictionary<int, DamageMesh>();
readonly HashSet<MeshFilter> seen = new HashSet<MeshFilter>();
readonly List<int> changed = new List<int>();
float nextScan, nextCheck, nextFreeCheck;
float nextScan, nextCheck, nextSafetyCheck;
// Mesh-Daten lesen ist teuer (große Arrays, Speicher-Müll) - nur nach einem Aufprall
readonly Dictionary<Rigidbody, Vector3> lastVelocity = new Dictionary<Rigidbody, Vector3>();
readonly Dictionary<Rigidbody, float> lastImpact = new Dictionary<Rigidbody, float>();
readonly List<Rigidbody> bodies = new List<Rigidbody>();
static List<Type> types;
public int Count { get { return meshes.Count; } }
@@ -41,27 +48,44 @@ namespace MSCO
{
meshes.Clear();
seen.Clear();
nextScan = 0f;
lastVelocity.Clear();
lastImpact.Clear();
bodies.Clear();
nextScan = -1f; // erster Suchlauf verteilt, siehe Update
}
public void Update(ObjectSync objects, int localId, Action<MsgType, byte[]> send)
{
if (nextScan < 0f) nextScan = Time.time + 1.0f; // nicht alle Sync-Teile im selben Frame suchen lassen
if (Time.time >= nextScan)
{
nextScan = Time.time + ScanInterval;
Scan();
}
// Aufprall erkennen: Geschwindigkeit ändert sich schlagartig (billig, jedes Frame)
for (int i = 0; i < bodies.Count; i++)
{
Rigidbody rb = bodies[i];
if (rb == null || rb.isKinematic) continue;
Vector3 v = rb.velocity, before;
if (lastVelocity.TryGetValue(rb, out before) && (v - before).sqrMagnitude > ImpactDeltaV * ImpactDeltaV)
lastImpact[rb] = Time.time;
lastVelocity[rb] = v;
}
if (Time.time < nextCheck) return;
nextCheck = Time.time + CheckInterval;
bool checkFree = Time.time >= nextFreeCheck;
if (checkFree) nextFreeCheck = Time.time + FreeCheckInterval;
bool safety = Time.time >= nextSafetyCheck;
if (safety) nextSafetyCheck = Time.time + SafetyCheckInterval;
foreach (DamageMesh m in meshes.Values)
{
if (m.Filter == null) continue;
int owner = objects.VehicleOwner(m.Root);
if (owner != SyncedBody.NoOwner && owner != localId) continue; // der Fahrer schickt die Schäden
if (owner == SyncedBody.NoOwner && !checkFree) continue;
float impact;
bool recentImpact = m.Body != null && lastImpact.TryGetValue(m.Body, out impact) && Time.time - impact < ImpactWindow;
if (!recentImpact && !safety) continue;
Mesh mesh = m.Filter.sharedMesh;
if (mesh == null) continue;
@@ -164,7 +188,9 @@ namespace MSCO
if (mesh == null || mesh.vertexCount == 0 || mesh.vertexCount > 65000) return;
int key = Hash(FsmSync.PathOf(mf.transform));
if (meshes.ContainsKey(key)) return; // nicht eindeutig
meshes[key] = new DamageMesh { Key = key, Root = mf.transform.root.name, Filter = mf, Known = mesh.vertices };
Rigidbody body = mf.transform.root.GetComponent<Rigidbody>();
meshes[key] = new DamageMesh { Key = key, Root = mf.transform.root.name, Filter = mf, Body = body, Known = mesh.vertices };
if (body != null && !bodies.Contains(body)) bodies.Add(body);
}
static bool IsVehicle(string root)