diff --git a/CHANGELOG.md b/CHANGELOG.md
index bde3924..46d4e48 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,32 +17,12 @@
- Konsole (F7): `model classic` zeigt wieder die MSCO-Figur, `model game` die MSC-Menschen; `rig` zeigt, wer welches
Modell hat. Findet MSCO den Körper nicht, bleibt automatisch die MSCO-Figur
-**Beifahrer**
-- **Automatisch einsteigen**: in die offene Beifahrertür an den Sitz treten - man setzt sich von selbst hin, keine Taste
- nötig. Aussteigen mit Leertaste oder der Beifahrer-Taste (P)
-- Geht jetzt auch, **wenn noch niemand fährt** (Sitzposition kommt aus dem Fahrersitz des Spiels); fährt danach jemand
- los, fährt man mit. Setzt man sich selbst ans Steuer, endet der Beifahrer-Modus
-
-**Server mit neuem Spiel**
-- Im Server-Panel (Welt & Backups) gibt es „Neues Spiel starten“: statt eines hochgeladenen Spielstands startet der
- erste Spieler ein frisches My-Summer-Car-Spiel, der Server übernimmt danach dessen Spielstand (braucht Server 2.9.3)
-
-**Launcher einklappen**
-- Knopf **–** oben rechts im Launcher: er klappt zu einer kleinen MSCO-Leiste oben zusammen, das Startmenü des Spiels
- (Optionen, Farbe, Musik ...) ist wieder sichtbar und bedienbar, die Menü-Musik läuft wieder. Klick auf die Leiste
- öffnet den Launcher. Bei Meldungen (getrennt, neu verbinden, Beitreten bestätigen) klappt er von selbst auf
-
-**Satsuma vollständiger synchron**
-- Zustand aller Satsuma-Teile wird jetzt übertragen - auch der Teile, die das Spiel außerhalb des Autos verwaltet
- (CARPARTS und die Teile-Datenbank): Verschleiß (Kolben, Kupplung, Getriebe, Lichtmaschine ...), beschädigte Teile
- (z.B. Kolben/Zylinderkopf nach Überhitzen), Reifen-Verschleiß, Öl im Ölwannen-Teil (Menge, Verschmutzung), Batterie-
- Ladung, Vergaser-/Zündeinstellungen, Fahrwerk, Kilometerstand. Maßgeblich ist, wer den Satsuma gerade fährt
-- **Lack**: Lackfarben werden übertragen und sofort auf das Teil gelegt
-- Teile, die erst während des Spiels angebaut werden (Radio, Sitze, Armaturen ...), werden jetzt auch erfasst - vorher
- wurden sie einmal als „nicht am Auto“ gemerkt und nie wieder angesehen
-- Mehr Bedienelemente: Handbremse, Sonnenblenden, Sitzlehne, Gurt, CD-Spieler, Innenlicht
-- **Später Beitretende** bekommen Schalter (Licht an, Motor läuft ...) und Jobs/Laden vom Server (ab Server 2.9.2);
- im P2P gleicht der Host die Schalter alle 15 s ab
+**Mitfahren**
+- **Wer im Fahrzeug ist, fährt mit - genau an seiner Stelle**: Kopf in der Kabine, in der offenen Tür oder auf der
+ Ladefläche -> die Position im Fahrzeug wird festgehalten, kein Umsetzen auf einen berechneten Sitz. Geht auch, wenn
+ noch niemand fährt. Aussteigen mit Leertaste oder der Beifahrer-Taste (P) - man steht dann auf der näheren Seite
+ neben dem Fahrzeug. P neben dem Auto setzt einen wie bisher auf den Beifahrersitz
+- Beim Ein-/Aussteigen am Fahrersitz und kurz nach dem Fahren wird man nicht festgehalten
**Satsuma zerfällt nicht mehr beim gemeinsamen Fahren**
- Hing das Spiel des Fahrers kurz (z.B. beim Speichern für den Server), gab der Mitfahrer das Auto nach 3 s frei: es
diff --git a/Passenger.cs b/Passenger.cs
index be75ae4..03da5ca 100644
--- a/Passenger.cs
+++ b/Passenger.cs
@@ -4,11 +4,10 @@ using UnityEngine;
namespace MSCO
{
///
- /// Beifahrer: Wer in die offene Beifahrertür an den Sitz tritt, setzt sich automatisch hin - egal ob schon jemand
- /// fährt oder das Auto noch steht (die Taste geht weiterhin, auch zum Aussteigen; Leertaste steigt ebenfalls aus).
- /// Der Sitz ist die Fahrerposition des Spiels (DriverHeadPivot), auf die andere Seite gespiegelt (bei Fahrzeugen mit
- /// Sitz in der Mitte, z.B. Traktor oder Moped: dahinter). Während der Fahrt wird der Spieler jedes Frame auf den Sitz
- /// gesetzt und dreht mit dem Fahrzeug mit; umschauen geht weiter.
+ /// Mitfahren: Wer mit dem Kopf im Fahrzeug ist (Kabine, offene Tür, Ladefläche ...), wird genau dort festgehalten und
+ /// fährt mit - egal ob schon jemand fährt oder das Auto noch steht. Aussteigen mit Leertaste oder der Beifahrer-Taste.
+ /// Steht man per Taste neben dem Auto, kommt man auf den Beifahrersitz (Fahrerposition des Spiels, gespiegelt).
+ /// Während der Fahrt wird der Spieler jedes Frame an seine Stelle im Fahrzeug gesetzt und dreht mit; umschauen geht.
///
public class Passenger
{
@@ -58,6 +57,12 @@ namespace MSCO
return "Got out";
}
if (cam == null || objects.IsDriving) return null;
+ SyncedBody inside = VehicleAround(objects, cam.transform.position);
+ if (inside != null)
+ {
+ Enter(inside, inside.Rb.transform.InverseTransformPoint(cam.transform.position), player);
+ return "Riding along (press again or Space to get out)";
+ }
SyncedBody best = null;
Vector3 bestSeat = Vector3.zero;
@@ -81,16 +86,16 @@ namespace MSCO
}
///
- /// Jedes Frame (zu Fuß): steht man mit dem Kopf am Beifahrersitz (in der offenen Tür), automatisch hinsetzen.
- /// Gibt einen Statustext zurück, wenn man eingestiegen ist.
+ /// Jedes Frame (zu Fuß): ist man mit dem Kopf IM Fahrzeug (Kabine, Ladefläche ...), wird die aktuelle Position im
+ /// Fahrzeug festgehalten - man fährt genau dort mit, wo man ist (kein Umsetzen auf einen berechneten Sitz).
+ /// Gibt einen Statustext zurück, wenn man "eingestiegen" ist.
///
public string AutoUpdate(Transform player, Camera cam, ObjectSync objects, IDictionary players)
{
if (objects.IsDriving) lastDriving = Time.time;
if (IsRiding || cam == null || player == null || objects.IsDriving) { dwellStart = -1f; return null; }
- // Steigt man auf der Fahrerseite ein, setzt das Spiel einen über die Sitze auf den Fahrersitz - dabei kommt der
- // Kopf kurz am Beifahrersitz vorbei. Solange das Spiel die Steuerung hat (CharacterController aus, Spieler an
- // ein Fahrzeug gehängt) oder kurz nach dem Fahren: nicht automatisch einsteigen.
+ // Beim Ein-/Aussteigen am Fahrersitz übernimmt das Spiel die Steuerung (CharacterController aus, Spieler an ein
+ // Fahrzeug gehängt) - dann und kurz nach dem Fahren nicht festhalten
if (Time.time - lastDriving < AfterDrivingPause) { dwellStart = -1f; return null; }
CharacterController cc = player.GetComponent();
if ((cc != null && !cc.enabled) || player.parent != null) { lastDriving = Time.time; dwellStart = -1f; return null; }
@@ -98,31 +103,14 @@ namespace MSCO
nextAutoCheck = Time.time + AutoCheckInterval;
Vector3 head = cam.transform.position;
- // Nach dem Aussteigen erst einmal weggehen, sonst säße man sofort wieder drin
+ // Nach dem Aussteigen erst einmal aus dem Fahrzeug heraus, sonst säße man sofort wieder drin
if (!autoArmed)
{
- if ((head - lastExitSeat).sqrMagnitude > AutoRearmRange * AutoRearmRange) autoArmed = true;
+ if (VehicleAround(objects, head) == null) autoArmed = true;
else return null;
}
- SyncedBody found = null;
- Vector3 foundSeat = Vector3.zero;
- foreach (SyncedBody b in objects.AllVehicles())
- {
- if (b.Rb == null || (b.Rb.position - head).sqrMagnitude > 8f * 8f) continue;
- Vector3? seat = SeatFor(b, objects, players);
- if (seat == null) continue;
- if ((b.Rb.transform.TransformPoint(seat.Value) - head).sqrMagnitude > AutoEnterRange * AutoEnterRange) continue;
- Vector3 local = b.Rb.transform.InverseTransformPoint(head);
- if (Mathf.Abs(local.x) > Mathf.Abs(seat.Value.x) + AutoEnterSide) continue;
- // Nur von der Beifahrerseite her: Kopf auf der Seite des Beifahrersitzes und nicht weiter innen als der Sitz
- // (wer auf der Fahrerseite einsteigt, ist auf der anderen Seite bzw. in der Mitte)
- if (Mathf.Abs(seat.Value.x) >= CenterSeatThreshold
- && (Mathf.Sign(local.x) != Mathf.Sign(seat.Value.x) || Mathf.Abs(local.x) < Mathf.Abs(seat.Value.x) - 0.1f)) continue;
- found = b;
- foundSeat = seat.Value;
- break;
- }
+ SyncedBody found = VehicleAround(objects, head);
if (found == null || found != dwellVehicle)
{
dwellVehicle = found;
@@ -132,8 +120,57 @@ namespace MSCO
if (Time.time - dwellStart < AutoEnterDwell) return null;
dwellStart = -1f;
dwellVehicle = null;
- Enter(found, foundSeat, player);
- return "Riding as passenger (Space or the passenger key to get out)";
+ Enter(found, found.Rb.transform.InverseTransformPoint(head), player); // genau hier festhalten
+ return "Riding along (Space or the passenger key to get out)";
+ }
+
+ /// Fahrzeug, in dessen Umriss der Kopf gerade ist (null = in keinem).
+ SyncedBody VehicleAround(ObjectSync objects, Vector3 head)
+ {
+ foreach (SyncedBody b in objects.AllVehicles())
+ {
+ if (b.Rb == null || (b.Rb.position - head).sqrMagnitude > 10f * 10f) continue;
+ Bounds inner = InnerBounds(b);
+ if (inner.Contains(b.Rb.transform.InverseTransformPoint(head))) return b;
+ }
+ return null;
+ }
+
+ readonly Dictionary innerBounds = new Dictionary();
+
+ ///
+ /// Innenraum des Fahrzeugs (lokal): Umriss aller festen Collider, seitlich und vorne/hinten etwas kleiner - neben
+ /// der geschlossenen Tür steht man noch draußen, erst in der offenen Tür bzw. drin ist man "im Auto".
+ ///
+ Bounds InnerBounds(SyncedBody b)
+ {
+ Bounds result;
+ if (innerBounds.TryGetValue(b.Rb, out result)) return result;
+ Transform t = b.Rb.transform;
+ bool any = false;
+ foreach (Collider c in b.Rb.GetComponentsInChildren())
+ {
+ if (c == null || c.isTrigger || !c.enabled) continue;
+ Bounds wb = c.bounds;
+ for (int i = 0; i < 8; i++)
+ {
+ Vector3 corner = wb.center + Vector3.Scale(wb.extents, new Vector3((i & 1) == 0 ? -1 : 1, (i & 2) == 0 ? -1 : 1, (i & 4) == 0 ? -1 : 1));
+ Vector3 lp = t.InverseTransformPoint(corner);
+ if (!any) { result = new Bounds(lp, Vector3.zero); any = true; }
+ else result.Encapsulate(lp);
+ }
+ }
+ if (!any) result = new Bounds(Vector3.zero, Vector3.zero);
+ else
+ {
+ Vector3 size = result.size;
+ // Weltachsen-Umrisse schräger Teile sind zu groß: seitlich 0.25 m, vorne/hinten 0.3 m kleiner,
+ // oben 0.6 m Luft (Kopf über dem Dach der Ladefläche / im offenen Fahrzeug)
+ result.size = new Vector3(Mathf.Max(0.1f, size.x - 0.5f), size.y + 0.6f, Mathf.Max(0.1f, size.z - 0.6f));
+ result.center += new Vector3(0f, 0.3f, 0f);
+ }
+ innerBounds[b.Rb] = result;
+ return result;
}
void Enter(SyncedBody b, Vector3 seat, Transform player)
@@ -222,8 +259,12 @@ namespace MSCO
lastExitSeat = vehicle.Rb.transform.TransformPoint(seatHeadLocal);
autoArmed = false;
// Auf der Seite des Sitzes neben das Fahrzeug stellen
+ // Zur näheren Seite neben das Fahrzeug (außerhalb seines Umrisses), auf Kopfhöhe von vorher
float side = seatHeadLocal.x >= 0f ? 1f : -1f;
- Vector3 outside = vehicle.Rb.transform.TransformPoint(seatHeadLocal + new Vector3(side * ExitSideOffset, 0f, 0f));
+ Bounds inner = InnerBounds(vehicle);
+ float x = inner.size.sqrMagnitude > 0f ? inner.center.x + side * (inner.extents.x + 0.85f) : seatHeadLocal.x + side * ExitSideOffset;
+ var local = new Vector3(x, seatHeadLocal.y, seatHeadLocal.z);
+ Vector3 outside = vehicle.Rb.transform.TransformPoint(local);
Vector3 cameraOffset = cam != null ? cam.transform.position - player.position : Vector3.up * 1.6f;
player.position = outside - cameraOffset + Vector3.up * 0.1f;
}
@@ -238,6 +279,7 @@ namespace MSCO
dwellVehicle = null;
dwellStart = -1f;
driverHeads.Clear();
+ innerBounds.Clear();
SetMovementEnabled(null, true);
}