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); }