Riding along: hold the current position inside the vehicle
Instead of snapping to a computed passenger seat, a player whose head is inside a vehicle's (shrunk) outline is held exactly there and moves with it; works for cabins, open doors and beds, with or without a driver. Getting out puts the player beside the vehicle on the nearer side. P next to the car still uses the passenger seat. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+6
-26
@@ -17,32 +17,12 @@
|
|||||||
- Konsole (F7): `model classic` zeigt wieder die MSCO-Figur, `model game` die MSC-Menschen; `rig` zeigt, wer welches
|
- 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
|
Modell hat. Findet MSCO den Körper nicht, bleibt automatisch die MSCO-Figur
|
||||||
|
|
||||||
**Beifahrer**
|
**Mitfahren**
|
||||||
- **Automatisch einsteigen**: in die offene Beifahrertür an den Sitz treten - man setzt sich von selbst hin, keine Taste
|
- **Wer im Fahrzeug ist, fährt mit - genau an seiner Stelle**: Kopf in der Kabine, in der offenen Tür oder auf der
|
||||||
nötig. Aussteigen mit Leertaste oder der Beifahrer-Taste (P)
|
Ladefläche -> die Position im Fahrzeug wird festgehalten, kein Umsetzen auf einen berechneten Sitz. Geht auch, wenn
|
||||||
- Geht jetzt auch, **wenn noch niemand fährt** (Sitzposition kommt aus dem Fahrersitz des Spiels); fährt danach jemand
|
noch niemand fährt. Aussteigen mit Leertaste oder der Beifahrer-Taste (P) - man steht dann auf der näheren Seite
|
||||||
los, fährt man mit. Setzt man sich selbst ans Steuer, endet der Beifahrer-Modus
|
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
|
||||||
**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
|
|
||||||
|
|
||||||
**Satsuma zerfällt nicht mehr beim gemeinsamen Fahren**
|
**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
|
- Hing das Spiel des Fahrers kurz (z.B. beim Speichern für den Server), gab der Mitfahrer das Auto nach 3 s frei: es
|
||||||
|
|||||||
+75
-33
@@ -4,11 +4,10 @@ using UnityEngine;
|
|||||||
namespace MSCO
|
namespace MSCO
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Beifahrer: Wer in die offene Beifahrertür an den Sitz tritt, setzt sich automatisch hin - egal ob schon jemand
|
/// Mitfahren: Wer mit dem Kopf im Fahrzeug ist (Kabine, offene Tür, Ladefläche ...), wird genau dort festgehalten und
|
||||||
/// fährt oder das Auto noch steht (die Taste geht weiterhin, auch zum Aussteigen; Leertaste steigt ebenfalls aus).
|
/// fährt mit - egal ob schon jemand fährt oder das Auto noch steht. Aussteigen mit Leertaste oder der Beifahrer-Taste.
|
||||||
/// Der Sitz ist die Fahrerposition des Spiels (DriverHeadPivot), auf die andere Seite gespiegelt (bei Fahrzeugen mit
|
/// Steht man per Taste neben dem Auto, kommt man auf den Beifahrersitz (Fahrerposition des Spiels, gespiegelt).
|
||||||
/// Sitz in der Mitte, z.B. Traktor oder Moped: dahinter). Während der Fahrt wird der Spieler jedes Frame auf den Sitz
|
/// Während der Fahrt wird der Spieler jedes Frame an seine Stelle im Fahrzeug gesetzt und dreht mit; umschauen geht.
|
||||||
/// gesetzt und dreht mit dem Fahrzeug mit; umschauen geht weiter.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class Passenger
|
public class Passenger
|
||||||
{
|
{
|
||||||
@@ -58,6 +57,12 @@ namespace MSCO
|
|||||||
return "Got out";
|
return "Got out";
|
||||||
}
|
}
|
||||||
if (cam == null || objects.IsDriving) return null;
|
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;
|
SyncedBody best = null;
|
||||||
Vector3 bestSeat = Vector3.zero;
|
Vector3 bestSeat = Vector3.zero;
|
||||||
@@ -81,16 +86,16 @@ namespace MSCO
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Jedes Frame (zu Fuß): steht man mit dem Kopf am Beifahrersitz (in der offenen Tür), automatisch hinsetzen.
|
/// Jedes Frame (zu Fuß): ist man mit dem Kopf IM Fahrzeug (Kabine, Ladefläche ...), wird die aktuelle Position im
|
||||||
/// Gibt einen Statustext zurück, wenn man eingestiegen ist.
|
/// 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.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string AutoUpdate(Transform player, Camera cam, ObjectSync objects, IDictionary<int, PlayerInfo> players)
|
public string AutoUpdate(Transform player, Camera cam, ObjectSync objects, IDictionary<int, PlayerInfo> players)
|
||||||
{
|
{
|
||||||
if (objects.IsDriving) lastDriving = Time.time;
|
if (objects.IsDriving) lastDriving = Time.time;
|
||||||
if (IsRiding || cam == null || player == null || objects.IsDriving) { dwellStart = -1f; return null; }
|
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
|
// Beim Ein-/Aussteigen am Fahrersitz übernimmt das Spiel die Steuerung (CharacterController aus, Spieler an ein
|
||||||
// Kopf kurz am Beifahrersitz vorbei. Solange das Spiel die Steuerung hat (CharacterController aus, Spieler an
|
// Fahrzeug gehängt) - dann und kurz nach dem Fahren nicht festhalten
|
||||||
// ein Fahrzeug gehängt) oder kurz nach dem Fahren: nicht automatisch einsteigen.
|
|
||||||
if (Time.time - lastDriving < AfterDrivingPause) { dwellStart = -1f; return null; }
|
if (Time.time - lastDriving < AfterDrivingPause) { dwellStart = -1f; return null; }
|
||||||
CharacterController cc = player.GetComponent<CharacterController>();
|
CharacterController cc = player.GetComponent<CharacterController>();
|
||||||
if ((cc != null && !cc.enabled) || player.parent != null) { lastDriving = Time.time; dwellStart = -1f; return null; }
|
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;
|
nextAutoCheck = Time.time + AutoCheckInterval;
|
||||||
Vector3 head = cam.transform.position;
|
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 (!autoArmed)
|
||||||
{
|
{
|
||||||
if ((head - lastExitSeat).sqrMagnitude > AutoRearmRange * AutoRearmRange) autoArmed = true;
|
if (VehicleAround(objects, head) == null) autoArmed = true;
|
||||||
else return null;
|
else return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
SyncedBody found = null;
|
SyncedBody found = VehicleAround(objects, head);
|
||||||
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;
|
|
||||||
}
|
|
||||||
if (found == null || found != dwellVehicle)
|
if (found == null || found != dwellVehicle)
|
||||||
{
|
{
|
||||||
dwellVehicle = found;
|
dwellVehicle = found;
|
||||||
@@ -132,8 +120,57 @@ namespace MSCO
|
|||||||
if (Time.time - dwellStart < AutoEnterDwell) return null;
|
if (Time.time - dwellStart < AutoEnterDwell) return null;
|
||||||
dwellStart = -1f;
|
dwellStart = -1f;
|
||||||
dwellVehicle = null;
|
dwellVehicle = null;
|
||||||
Enter(found, foundSeat, player);
|
Enter(found, found.Rb.transform.InverseTransformPoint(head), player); // genau hier festhalten
|
||||||
return "Riding as passenger (Space or the passenger key to get out)";
|
return "Riding along (Space or the passenger key to get out)";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Fahrzeug, in dessen Umriss der Kopf gerade ist (null = in keinem).</summary>
|
||||||
|
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<Rigidbody, Bounds> innerBounds = new Dictionary<Rigidbody, Bounds>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 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".
|
||||||
|
/// </summary>
|
||||||
|
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<Collider>())
|
||||||
|
{
|
||||||
|
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)
|
void Enter(SyncedBody b, Vector3 seat, Transform player)
|
||||||
@@ -222,8 +259,12 @@ namespace MSCO
|
|||||||
lastExitSeat = vehicle.Rb.transform.TransformPoint(seatHeadLocal);
|
lastExitSeat = vehicle.Rb.transform.TransformPoint(seatHeadLocal);
|
||||||
autoArmed = false;
|
autoArmed = false;
|
||||||
// Auf der Seite des Sitzes neben das Fahrzeug stellen
|
// 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;
|
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;
|
Vector3 cameraOffset = cam != null ? cam.transform.position - player.position : Vector3.up * 1.6f;
|
||||||
player.position = outside - cameraOffset + Vector3.up * 0.1f;
|
player.position = outside - cameraOffset + Vector3.up * 0.1f;
|
||||||
}
|
}
|
||||||
@@ -238,6 +279,7 @@ namespace MSCO
|
|||||||
dwellVehicle = null;
|
dwellVehicle = null;
|
||||||
dwellStart = -1f;
|
dwellStart = -1f;
|
||||||
driverHeads.Clear();
|
driverHeads.Clear();
|
||||||
|
innerBounds.Clear();
|
||||||
SetMovementEnabled(null, true);
|
SetMovementEnabled(null, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user