From bdd86336c20f97dab480666c18412c21abe4d952 Mon Sep 17 00:00:00 2001 From: MrMaho Date: Sun, 27 Sep 2026 09:18:39 +0200 Subject: [PATCH] Remote-driven car: keep attached parts on unbreakable joints instead of switching them kinematic (that broke the joints and the car fell apart); passenger range measured to the car body Co-Authored-By: Claude Opus 5.5 --- ObjectSync.cs | 48 +++++++++++++++++++++++------------------------- Passenger.cs | 20 ++++++++++++++++++-- 2 files changed, 41 insertions(+), 27 deletions(-) diff --git a/ObjectSync.cs b/ObjectSync.cs index 65f4d85..8805798 100644 --- a/ObjectSync.cs +++ b/ObjectSync.cs @@ -22,8 +22,8 @@ namespace MSCO public int OwnerId = NoOwner; public bool OwnerActive; // Besitzer hält/fährt es gerade aktiv public bool WasKinematic; // Zustand bevor ein anderer Spieler es übernommen hat - // Angebaute Teile eines Fahrzeugs (eigene Rigidbodies mit Gelenken), solange ein anderer fährt: kinematisch - public Dictionary FrozenParts; + // Gelenke der angebauten Teile, solange ein anderer das Fahrzeug fährt: unzerbrechlich (vorherige Bruchkraft/-moment) + public Dictionary HeldJoints; public float NextPartScan; public float StillTime; public float NoMotionClaimUntil; // kurz nach einer Übergabe nicht wegen Nachfedern/Nachrollen übernehmen @@ -305,7 +305,7 @@ namespace MSCO foreach (SyncedBody b in byId.Values) { if (!IsRemoteOwned(b) || b.Rb == null || b.Attached) continue; - if (b.IsVehicle && Time.time >= b.NextPartScan) FreezeParts(b); + if (b.IsVehicle && Time.time >= b.NextPartScan) HoldParts(b); if (Time.time - b.AvatarHoldTime < 0.2f) continue; // sitzt gerade in der Hand eines Mitspielers if ((b.Rb.position - b.TargetPos).sqrMagnitude > SnapDistance * SnapDistance) { @@ -393,45 +393,43 @@ namespace MSCO } /// - /// Ein anderer Spieler bewegt das Objekt: bei uns nur noch folgen (kinematisch). Bei Fahrzeugen auch alle - /// angebauten Teile - die hängen in MSC mit Gelenken (mit Bruchkraft) am Auto. Würde nur das Auto ruckartig - /// versetzt, rissen die Gelenke und das Auto fiele auseinander. + /// Ein anderer Spieler bewegt das Objekt: bei uns nur noch folgen (kinematisch). Bei Fahrzeugen hängen die + /// angebauten Teile (Türen, Kotflügel, Sitze ...) mit Gelenken mit Bruchkraft am Auto. Die Teile dürfen NICHT + /// selbst kinematisch geschaltet werden (das bricht die Gelenke sofort, MSC wertet es als "Teil abgefallen"), + /// und das ruckartige Folgen würde sie ebenfalls abreißen. Deshalb werden ihre Gelenke solange unzerbrechlich. /// void FreezeForRemote(SyncedBody b) { b.WasKinematic = b.Rb.isKinematic; b.Rb.isKinematic = true; - if (b.IsVehicle) FreezeParts(b); + if (b.IsVehicle) HoldParts(b); } - void FreezeParts(SyncedBody b) + void HoldParts(SyncedBody b) { - b.NextPartScan = Time.time + 1f; - if (b.FrozenParts == null) b.FrozenParts = new Dictionary(); - foreach (Rigidbody part in b.Rb.GetComponentsInChildren()) + b.NextPartScan = Time.time + 1f; // später angebaute Teile ebenfalls + if (b.HeldJoints == null) b.HeldJoints = new Dictionary(); + foreach (Joint j in b.Rb.GetComponentsInChildren(true)) { - if (part == b.Rb || b.FrozenParts.ContainsKey(part)) continue; - b.FrozenParts[part] = part.isKinematic; - part.isKinematic = true; + if (j == null || b.HeldJoints.ContainsKey(j)) continue; + b.HeldJoints[j] = new Vector2(j.breakForce, j.breakTorque); + j.breakForce = Mathf.Infinity; + j.breakTorque = Mathf.Infinity; } } - /// Wieder selbst simulieren: Auto und angebaute Teile zurück auf ihren vorherigen Zustand. + /// Wieder selbst simulieren: Auto zurück auf seinen Zustand, Gelenke wieder mit ihrer Bruchkraft. void RestoreKinematic(SyncedBody b) { if (b.Rb != null) b.Rb.isKinematic = b.WasKinematic; - if (b.FrozenParts == null) return; - foreach (KeyValuePair kv in b.FrozenParts) + if (b.HeldJoints == null) return; + foreach (KeyValuePair kv in b.HeldJoints) { - if (kv.Key == null) continue; - kv.Key.isKinematic = kv.Value; - if (!kv.Value) - { - kv.Key.velocity = Vector3.zero; // keinen Schwung aus dem Folgen mitnehmen - kv.Key.angularVelocity = Vector3.zero; - } + if (kv.Key == null) continue; // Teil wurde inzwischen abgebaut + kv.Key.breakForce = kv.Value.x; + kv.Key.breakTorque = kv.Value.y; } - b.FrozenParts = null; + b.HeldJoints = null; } void SendOwnedStates() diff --git a/Passenger.cs b/Passenger.cs index 0616242..7c9d27d 100644 --- a/Passenger.cs +++ b/Passenger.cs @@ -11,7 +11,7 @@ namespace MSCO /// public class Passenger { - const float EnterRange = 5f; // so nah muss man am Fahrzeug stehen (Abstand zur Fahrzeugmitte) + const float EnterRange = 3f; // so nah muss man an der Karosserie stehen const float CenterSeatThreshold = 0.2f; const float BehindCenterSeat = 0.7f; const float ExitSideOffset = 1.4f; // beim Aussteigen so weit neben den Sitz @@ -44,7 +44,7 @@ namespace MSCO float nearest = float.MaxValue; foreach (SyncedBody b in driven) { - float d = (b.Rb.worldCenterOfMass - player.position).sqrMagnitude; + float d = DistanceToVehicleSq(b, player.position); nearest = Mathf.Min(nearest, d); PlayerInfo owner; if (!players.TryGetValue(b.OwnerId, out owner) || owner.Avatar == null) continue; @@ -69,6 +69,22 @@ namespace MSCO return "Riding as passenger (press again to get out)"; } + /// + /// Abstand zur nächsten Stelle der Karosserie (Collider), nicht zum Schwerpunkt - den legt MSC teils weit weg, + /// dann hieß es "26 m entfernt", obwohl man direkt an der Tür stand. + /// + static float DistanceToVehicleSq(SyncedBody b, Vector3 point) + { + float best = (b.Rb.position - point).sqrMagnitude; + foreach (Collider c in b.Rb.GetComponentsInChildren()) + { + if (c == null || !c.enabled || c.isTrigger) continue; + float d = (c.bounds.ClosestPoint(point) - point).sqrMagnitude; + if (d < best) best = d; + } + return best; + } + /// Nach der Physik (LateUpdate): Spieler auf den Sitz setzen und mitdrehen. public void LateUpdate(Transform player, Camera cam, ObjectSync objects) {