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 <noreply@anthropic.com>
This commit is contained in:
2026-09-27 09:18:39 +02:00
co-authored by Claude Opus 5.5
parent ce4aa7e0a6
commit bdd86336c2
2 changed files with 41 additions and 27 deletions
+23 -25
View File
@@ -22,8 +22,8 @@ namespace MSCO
public int OwnerId = NoOwner; public int OwnerId = NoOwner;
public bool OwnerActive; // Besitzer hält/fährt es gerade aktiv public bool OwnerActive; // Besitzer hält/fährt es gerade aktiv
public bool WasKinematic; // Zustand bevor ein anderer Spieler es übernommen hat 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 // Gelenke der angebauten Teile, solange ein anderer das Fahrzeug fährt: unzerbrechlich (vorherige Bruchkraft/-moment)
public Dictionary<Rigidbody, bool> FrozenParts; public Dictionary<Joint, Vector2> HeldJoints;
public float NextPartScan; public float NextPartScan;
public float StillTime; public float StillTime;
public float NoMotionClaimUntil; // kurz nach einer Übergabe nicht wegen Nachfedern/Nachrollen übernehmen public float NoMotionClaimUntil; // kurz nach einer Übergabe nicht wegen Nachfedern/Nachrollen übernehmen
@@ -305,7 +305,7 @@ namespace MSCO
foreach (SyncedBody b in byId.Values) foreach (SyncedBody b in byId.Values)
{ {
if (!IsRemoteOwned(b) || b.Rb == null || b.Attached) continue; 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 (Time.time - b.AvatarHoldTime < 0.2f) continue; // sitzt gerade in der Hand eines Mitspielers
if ((b.Rb.position - b.TargetPos).sqrMagnitude > SnapDistance * SnapDistance) if ((b.Rb.position - b.TargetPos).sqrMagnitude > SnapDistance * SnapDistance)
{ {
@@ -393,45 +393,43 @@ namespace MSCO
} }
/// <summary> /// <summary>
/// Ein anderer Spieler bewegt das Objekt: bei uns nur noch folgen (kinematisch). Bei Fahrzeugen auch alle /// Ein anderer Spieler bewegt das Objekt: bei uns nur noch folgen (kinematisch). Bei Fahrzeugen hängen die
/// angebauten Teile - die hängen in MSC mit Gelenken (mit Bruchkraft) am Auto. Würde nur das Auto ruckartig /// angebauten Teile (Türen, Kotflügel, Sitze ...) mit Gelenken mit Bruchkraft am Auto. Die Teile dürfen NICHT
/// versetzt, rissen die Gelenke und das Auto fiele auseinander. /// 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.
/// </summary> /// </summary>
void FreezeForRemote(SyncedBody b) void FreezeForRemote(SyncedBody b)
{ {
b.WasKinematic = b.Rb.isKinematic; b.WasKinematic = b.Rb.isKinematic;
b.Rb.isKinematic = true; 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; b.NextPartScan = Time.time + 1f; // später angebaute Teile ebenfalls
if (b.FrozenParts == null) b.FrozenParts = new Dictionary<Rigidbody, bool>(); if (b.HeldJoints == null) b.HeldJoints = new Dictionary<Joint, Vector2>();
foreach (Rigidbody part in b.Rb.GetComponentsInChildren<Rigidbody>()) foreach (Joint j in b.Rb.GetComponentsInChildren<Joint>(true))
{ {
if (part == b.Rb || b.FrozenParts.ContainsKey(part)) continue; if (j == null || b.HeldJoints.ContainsKey(j)) continue;
b.FrozenParts[part] = part.isKinematic; b.HeldJoints[j] = new Vector2(j.breakForce, j.breakTorque);
part.isKinematic = true; j.breakForce = Mathf.Infinity;
j.breakTorque = Mathf.Infinity;
} }
} }
/// <summary>Wieder selbst simulieren: Auto und angebaute Teile zurück auf ihren vorherigen Zustand.</summary> /// <summary>Wieder selbst simulieren: Auto zurück auf seinen Zustand, Gelenke wieder mit ihrer Bruchkraft.</summary>
void RestoreKinematic(SyncedBody b) void RestoreKinematic(SyncedBody b)
{ {
if (b.Rb != null) b.Rb.isKinematic = b.WasKinematic; if (b.Rb != null) b.Rb.isKinematic = b.WasKinematic;
if (b.FrozenParts == null) return; if (b.HeldJoints == null) return;
foreach (KeyValuePair<Rigidbody, bool> kv in b.FrozenParts) foreach (KeyValuePair<Joint, Vector2> kv in b.HeldJoints)
{ {
if (kv.Key == null) continue; if (kv.Key == null) continue; // Teil wurde inzwischen abgebaut
kv.Key.isKinematic = kv.Value; kv.Key.breakForce = kv.Value.x;
if (!kv.Value) kv.Key.breakTorque = kv.Value.y;
{
kv.Key.velocity = Vector3.zero; // keinen Schwung aus dem Folgen mitnehmen
kv.Key.angularVelocity = Vector3.zero;
}
} }
b.FrozenParts = null; b.HeldJoints = null;
} }
void SendOwnedStates() void SendOwnedStates()
+18 -2
View File
@@ -11,7 +11,7 @@ namespace MSCO
/// </summary> /// </summary>
public class Passenger 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 CenterSeatThreshold = 0.2f;
const float BehindCenterSeat = 0.7f; const float BehindCenterSeat = 0.7f;
const float ExitSideOffset = 1.4f; // beim Aussteigen so weit neben den Sitz const float ExitSideOffset = 1.4f; // beim Aussteigen so weit neben den Sitz
@@ -44,7 +44,7 @@ namespace MSCO
float nearest = float.MaxValue; float nearest = float.MaxValue;
foreach (SyncedBody b in driven) foreach (SyncedBody b in driven)
{ {
float d = (b.Rb.worldCenterOfMass - player.position).sqrMagnitude; float d = DistanceToVehicleSq(b, player.position);
nearest = Mathf.Min(nearest, d); nearest = Mathf.Min(nearest, d);
PlayerInfo owner; PlayerInfo owner;
if (!players.TryGetValue(b.OwnerId, out owner) || owner.Avatar == null) continue; 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)"; return "Riding as passenger (press again to get out)";
} }
/// <summary>
/// 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.
/// </summary>
static float DistanceToVehicleSq(SyncedBody b, Vector3 point)
{
float best = (b.Rb.position - point).sqrMagnitude;
foreach (Collider c in b.Rb.GetComponentsInChildren<Collider>())
{
if (c == null || !c.enabled || c.isTrigger) continue;
float d = (c.bounds.ClosestPoint(point) - point).sqrMagnitude;
if (d < best) best = d;
}
return best;
}
/// <summary>Nach der Physik (LateUpdate): Spieler auf den Sitz setzen und mitdrehen.</summary> /// <summary>Nach der Physik (LateUpdate): Spieler auf den Sitz setzen und mitdrehen.</summary>
public void LateUpdate(Transform player, Camera cam, ObjectSync objects) public void LateUpdate(Transform player, Camera cam, ObjectSync objects)
{ {