Nicer player figure: chest + belly instead of box and ball, bigger head, straight legs, flat belt, cap brim down, matte materials; free vehicles only sync big value jumps

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-27 00:04:02 +02:00
co-authored by Claude Opus 5.5
parent dfdb78db17
commit c3f84895a7
2 changed files with 60 additions and 48 deletions
+46 -42
View File
@@ -16,7 +16,7 @@ namespace MSCO
const float MaxHeadPitch = 55f; const float MaxHeadPitch = 55f;
// Körpermaße (Meter) // Körpermaße (Meter)
const float ThighLength = 0.45f, ShinLength = 0.45f; const float ThighLength = 0.41f, ShinLength = 0.41f; // im Stehen fast gestreckt
const float UpperArmLength = 0.3f, ForearmLength = 0.29f; const float UpperArmLength = 0.3f, ForearmLength = 0.29f;
const float TorsoLength = 0.55f; // Hüfte bis Hals const float TorsoLength = 0.55f; // Hüfte bis Hals
const float HipWidth = 0.1f, ShoulderWidth = 0.21f; const float HipWidth = 0.1f, ShoulderWidth = 0.21f;
@@ -88,9 +88,9 @@ namespace MSCO
// Körper: Karohemd/Jacke, Jeans, Gürtel, leichter Bauch (wie die Leute in MSC) // Körper: Karohemd/Jacke, Jeans, Gürtel, leichter Bauch (wie die Leute in MSC)
pelvis = Capsule("Pelvis", r, Jeans); pelvis = Capsule("Pelvis", r, Jeans);
torso = Textured(Capsule("Torso", r, Color.white), plaid, new Vector2(2f, 3f)); torso = Textured(Capsule("Chest", r, Color.white), plaid, new Vector2(2f, 2f));
belly = Textured(Sphere("Belly", r, Color.white, 1f), plaid, new Vector2(2f, 2f)); belly = Textured(Capsule("Belly", r, Color.white), plaid, new Vector2(2f, 1f));
belt = Capsule("Belt", r, new Color(0.2f, 0.13f, 0.08f)); belt = Box("Belt", r, new Color(0.2f, 0.13f, 0.08f));
buckle = Box("Buckle", r, Metal); buckle = Box("Buckle", r, Metal);
placket = Box("Buttons", r, jacketDark); placket = Box("Buttons", r, jacketDark);
collar = Capsule("Collar", r, jacketDark); collar = Capsule("Collar", r, jacketDark);
@@ -112,55 +112,55 @@ namespace MSCO
headPivot = new GameObject("Head").transform; headPivot = new GameObject("Head").transform;
headPivot.SetParent(r, false); headPivot.SetParent(r, false);
Transform skull = Sphere("Skull", headPivot, Skin, 0.24f); Transform skull = Sphere("Skull", headPivot, Skin, 0.24f);
skull.localScale = new Vector3(0.21f, 0.255f, 0.235f); skull.localScale = new Vector3(0.225f, 0.27f, 0.25f);
Transform jaw = Sphere("Jaw", headPivot, Skin, 0.2f); Transform jaw = Sphere("Jaw", headPivot, Skin, 0.2f);
jaw.localPosition = new Vector3(0f, -0.065f, 0.02f); jaw.localPosition = new Vector3(0f, -0.07f, 0.022f);
jaw.localScale = new Vector3(0.18f, 0.13f, 0.19f); jaw.localScale = new Vector3(0.19f, 0.14f, 0.2f);
Transform hair = Sphere("Hair", headPivot, hairColor, 0.24f); Transform hair = Sphere("Hair", headPivot, hairColor, 0.24f);
hair.localPosition = new Vector3(0f, 0.025f, -0.03f); hair.localPosition = new Vector3(0f, 0.028f, -0.032f);
hair.localScale = new Vector3(0.22f, 0.24f, 0.22f); hair.localScale = new Vector3(0.235f, 0.255f, 0.235f);
for (int i = 0; i < 2; i++) for (int i = 0; i < 2; i++)
{ {
float side = i == 0 ? -1f : 1f; float side = i == 0 ? -1f : 1f;
Transform ear = Sphere("Ear", headPivot, Skin * 0.96f, 0.05f); Transform ear = Sphere("Ear", headPivot, Skin * 0.96f, 0.05f);
ear.localPosition = new Vector3(side * 0.105f, -0.005f, 0f); ear.localPosition = new Vector3(side * 0.112f, -0.005f, 0f);
ear.localScale = new Vector3(0.035f, 0.065f, 0.05f); ear.localScale = new Vector3(0.035f, 0.065f, 0.05f);
Transform eye = Sphere("Eye", headPivot, Dark, 0.028f); Transform eye = Sphere("Eye", headPivot, Dark, 0.026f);
eye.localPosition = new Vector3(side * 0.042f, 0.018f, 0.105f); eye.localPosition = new Vector3(side * 0.045f, 0.018f, 0.112f);
Transform brow = Box("Brow", headPivot, hairColor); Transform brow = Box("Brow", headPivot, hairColor);
brow.localPosition = new Vector3(side * 0.043f, 0.045f, 0.108f); brow.localPosition = new Vector3(side * 0.046f, 0.046f, 0.117f);
brow.localScale = new Vector3(0.045f, 0.012f, 0.012f); brow.localScale = new Vector3(0.045f, 0.012f, 0.012f);
brow.localRotation = Quaternion.Euler(0f, 0f, side * -8f); brow.localRotation = Quaternion.Euler(0f, 0f, side * -8f);
} }
Transform nose = Box("Nose", headPivot, Skin * 0.93f); Transform nose = Box("Nose", headPivot, Skin * 0.93f);
nose.localPosition = new Vector3(0f, -0.012f, 0.12f); nose.localPosition = new Vector3(0f, -0.012f, 0.128f);
nose.localScale = new Vector3(0.035f, 0.055f, 0.045f); nose.localScale = new Vector3(0.035f, 0.055f, 0.045f);
// Der typische Schnurrbart // Der typische Schnurrbart
Transform mustache = Box("Mustache", headPivot, hairColor); Transform mustache = Box("Mustache", headPivot, hairColor);
mustache.localPosition = new Vector3(0f, -0.052f, 0.113f); mustache.localPosition = new Vector3(0f, -0.054f, 0.12f);
mustache.localScale = new Vector3(0.085f, 0.024f, 0.03f); mustache.localScale = new Vector3(0.085f, 0.024f, 0.03f);
Transform mouth = Box("Mouth", headPivot, new Color(0.45f, 0.25f, 0.22f)); Transform mouth = Box("Mouth", headPivot, new Color(0.45f, 0.25f, 0.22f));
mouth.localPosition = new Vector3(0f, -0.078f, 0.108f); mouth.localPosition = new Vector3(0f, -0.082f, 0.114f);
mouth.localScale = new Vector3(0.05f, 0.01f, 0.01f); mouth.localScale = new Vector3(0.05f, 0.01f, 0.01f);
if (sunglasses) if (sunglasses)
{ {
Transform glasses = Box("Sunglasses", headPivot, Dark); Transform glasses = Box("Sunglasses", headPivot, Dark);
glasses.localPosition = new Vector3(0f, 0.018f, 0.113f); glasses.localPosition = new Vector3(0f, 0.018f, 0.121f);
glasses.localScale = new Vector3(0.14f, 0.035f, 0.012f); glasses.localScale = new Vector3(0.15f, 0.035f, 0.012f);
} }
if (wearsCap) if (wearsCap)
{ {
// Kappe ("lippis") in der Jackenfarbe // Kappe ("lippis") in der Jackenfarbe
Transform cap = Sphere("Cap", headPivot, jacketDark, 0.25f); Transform cap = Sphere("Cap", headPivot, jacketDark, 0.25f);
cap.localPosition = new Vector3(0f, 0.075f, -0.005f); cap.localPosition = new Vector3(0f, 0.08f, -0.005f);
cap.localScale = new Vector3(0.235f, 0.15f, 0.245f); cap.localScale = new Vector3(0.25f, 0.16f, 0.26f);
Transform brim = Box("CapBrim", headPivot, jacketDark); Transform brim = Box("CapBrim", headPivot, jacketDark);
brim.localPosition = new Vector3(0f, 0.078f, 0.13f); brim.localPosition = new Vector3(0f, 0.07f, 0.15f);
brim.localScale = new Vector3(0.19f, 0.018f, 0.11f); brim.localScale = new Vector3(0.2f, 0.014f, 0.1f);
brim.localRotation = Quaternion.Euler(-10f, 0f, 0f); brim.localRotation = Quaternion.Euler(8f, 0f, 0f); // Schirm leicht nach unten
Transform patch = Box("CapPatch", headPivot, new Color(0.9f, 0.88f, 0.8f)); Transform patch = Box("CapPatch", headPivot, new Color(0.9f, 0.88f, 0.8f));
patch.localPosition = new Vector3(0f, 0.105f, 0.112f); patch.localPosition = new Vector3(0f, 0.115f, 0.118f);
patch.localScale = new Vector3(0.06f, 0.035f, 0.01f); patch.localScale = new Vector3(0.06f, 0.035f, 0.01f);
patch.localRotation = Quaternion.Euler(-25f, 0f, 0f); patch.localRotation = Quaternion.Euler(-25f, 0f, 0f);
} }
@@ -220,6 +220,9 @@ namespace MSCO
Destroy(go.GetComponent<Collider>()); // stört sonst Physik/Trigger Destroy(go.GetComponent<Collider>()); // stört sonst Physik/Trigger
var renderer = go.GetComponent<Renderer>(); var renderer = go.GetComponent<Renderer>();
renderer.material.color = color; renderer.material.color = color;
// Matt wie Stoff/Haut statt glänzendem Plastik (Standard-Shader)
if (renderer.material.HasProperty("_Glossiness")) renderer.material.SetFloat("_Glossiness", 0.08f);
if (renderer.material.HasProperty("_Metallic")) renderer.material.SetFloat("_Metallic", 0f);
renderer.receiveShadows = true; renderer.receiveShadows = true;
go.transform.SetParent(parent, false); go.transform.SetParent(parent, false);
return go.transform; return go.transform;
@@ -306,18 +309,19 @@ namespace MSCO
// Rumpf // Rumpf
Quaternion torsoRot = Quaternion.LookRotation(forward, up); Quaternion torsoRot = Quaternion.LookRotation(forward, up);
Segment(torso, hip + up * 0.08f, neckBase, 0.4f, 0.25f); // Brust (breit oben) und Bauch (etwas tiefer = leichter Bauch) - fließend statt Klotz mit Kugel
belly.localPosition = hip + up * 0.2f + forward * 0.06f; Segment(torso, hip + up * 0.26f, neckBase - up * 0.04f, 0.37f, 0.22f);
belly.localRotation = torsoRot; Segment(belly, hip + up * 0.07f + forward * 0.015f, hip + up * 0.3f + forward * 0.015f, 0.33f, 0.25f);
belly.localScale = new Vector3(0.34f, 0.28f, 0.27f); belt.localPosition = hip + up * 0.06f;
Segment(belt, hip + up * 0.05f, hip + up * 0.1f, 0.38f, 0.26f); belt.localRotation = torsoRot;
buckle.localPosition = hip + up * 0.075f + forward * 0.13f; belt.localScale = new Vector3(0.345f, 0.045f, 0.275f);
buckle.localPosition = hip + up * 0.06f + forward * 0.14f;
buckle.localRotation = torsoRot; buckle.localRotation = torsoRot;
buckle.localScale = new Vector3(0.06f, 0.045f, 0.015f); buckle.localScale = new Vector3(0.055f, 0.04f, 0.012f);
placket.localPosition = Vector3.Lerp(hip + up * 0.1f, neckBase, 0.62f) + forward * 0.122f; placket.localPosition = Vector3.Lerp(hip + up * 0.12f, neckBase, 0.6f) + forward * 0.124f;
placket.localRotation = torsoRot; placket.localRotation = torsoRot;
placket.localScale = new Vector3(0.025f, 0.26f, 0.01f); placket.localScale = new Vector3(0.022f, 0.3f, 0.01f);
Segment(pelvis, hip - right * HipWidth, hip + right * HipWidth, 0.22f, 0.24f); Segment(pelvis, hip - right * (HipWidth * 0.8f) - up * 0.02f, hip + right * (HipWidth * 0.8f) - up * 0.02f, 0.2f, 0.22f);
Segment(collar, neckBase - up * 0.02f, neckBase + up * 0.035f, 0.17f, 0.15f); Segment(collar, neckBase - up * 0.02f, neckBase + up * 0.035f, 0.17f, 0.15f);
Segment(neck, neckBase, neckTop, 0.1f, 0.1f); Segment(neck, neckBase, neckTop, 0.1f, 0.1f);
@@ -339,8 +343,8 @@ namespace MSCO
else else
ankle = new Vector3(HipWidth * side, AnkleHeight + Mathf.Max(0f, Mathf.Cos(walkPhase + i * Mathf.PI)) * 0.07f * walkAmount, phase * 0.3f); ankle = new Vector3(HipWidth * side, AnkleHeight + Mathf.Max(0f, Mathf.Cos(walkPhase + i * Mathf.PI)) * 0.07f * walkAmount, phase * 0.3f);
Vector3 knee = Joint(hipJoint, ankle, ThighLength, ShinLength, Vector3.forward); Vector3 knee = Joint(hipJoint, ankle, ThighLength, ShinLength, Vector3.forward);
Segment(thigh[i], hipJoint, knee, 0.17f, 0.17f); Segment(thigh[i], hipJoint, knee, 0.15f, 0.16f);
Segment(shin[i], knee, ankle, 0.14f, 0.14f); Segment(shin[i], knee, ankle, 0.12f, 0.13f);
boot[i].localPosition = ankle + new Vector3(0f, -0.015f, 0.05f); boot[i].localPosition = ankle + new Vector3(0f, -0.015f, 0.05f);
boot[i].localRotation = Quaternion.identity; boot[i].localRotation = Quaternion.identity;
boot[i].localScale = new Vector3(0.12f, 0.1f, 0.27f); boot[i].localScale = new Vector3(0.12f, 0.1f, 0.27f);
@@ -358,7 +362,7 @@ namespace MSCO
else if (last.Seated) else if (last.Seated)
handTarget = shoulder + new Vector3(-side * 0.05f, -0.3f, 0.38f); // Hände nach vorne (Lenkrad) handTarget = shoulder + new Vector3(-side * 0.05f, -0.3f, 0.38f); // Hände nach vorne (Lenkrad)
else else
handTarget = shoulder + new Vector3(side * 0.03f, -(UpperArmLength + ForearmLength) + 0.04f, -phase * 0.22f); handTarget = shoulder + new Vector3(side * 0.06f, -(UpperArmLength + ForearmLength) + 0.05f, 0.02f - phase * 0.22f);
// Nicht weiter als die Armlänge // Nicht weiter als die Armlänge
Vector3 reach = handTarget - shoulder; Vector3 reach = handTarget - shoulder;
@@ -366,12 +370,12 @@ namespace MSCO
if (reach.magnitude > maxReach) handTarget = shoulder + reach.normalized * maxReach; if (reach.magnitude > maxReach) handTarget = shoulder + reach.normalized * maxReach;
Vector3 elbow = Joint(shoulder, handTarget, UpperArmLength, ForearmLength, new Vector3(side * 0.4f, -0.2f, -1f)); Vector3 elbow = Joint(shoulder, handTarget, UpperArmLength, ForearmLength, new Vector3(side * 0.4f, -0.2f, -1f));
Segment(upperArm[i], shoulder, elbow, 0.12f, 0.12f); Segment(upperArm[i], shoulder, elbow, 0.11f, 0.11f);
Segment(forearm[i], elbow, handTarget, 0.11f, 0.11f); Segment(forearm[i], elbow, handTarget, 0.095f, 0.095f);
Vector3 wrist = Vector3.Lerp(elbow, handTarget, 0.86f); Segment(cuff[i], Vector3.Lerp(elbow, handTarget, 0.82f), Vector3.Lerp(elbow, handTarget, 0.9f), 0.105f, 0.105f);
Segment(cuff[i], wrist, Vector3.Lerp(elbow, handTarget, 0.94f), 0.12f, 0.12f);
hand[i].localPosition = handTarget; hand[i].localPosition = handTarget;
hand[i].localScale = new Vector3(0.085f, 0.1f, 0.1f); hand[i].localRotation = Quaternion.LookRotation(Vector3.forward, (handTarget - elbow).sqrMagnitude > 0f ? elbow - handTarget : Vector3.up);
hand[i].localScale = new Vector3(0.075f, 0.115f, 0.1f);
} }
} }
+14 -6
View File
@@ -74,11 +74,16 @@ namespace MSCO
outgoing.Clear(); outgoing.Clear();
foreach (Var v in vars.Values) foreach (Var v in vars.Values)
{ {
if (!Changed(v)) continue; // Fährt/schiebt ein anderer das Fahrzeug, ist seine Version maßgeblich - nichts senden
Remember(v);
// Fährt/schiebt ein anderer das Fahrzeug, ist seine Version maßgeblich
int owner = objects.VehicleOwner(v.Root); int owner = objects.VehicleOwner(v.Root);
if (owner == SyncedBody.NoOwner || owner == localId) outgoing.Add(v); if (owner != SyncedBody.NoOwner && owner != localId) { if (Changed(v, false)) Remember(v); continue; }
// Eigenes Fahrzeug: jede Änderung. Freies Fahrzeug: nur deutliche Sprünge (Öl nachfüllen, tanken ...) -
// langsames Abdriften (z.B. Abkühlen) rechnet jeder selbst, sonst überschreiben sich alle gegenseitig
bool big = owner == SyncedBody.NoOwner;
if (!Changed(v, big)) continue;
Remember(v);
outgoing.Add(v);
} }
for (int start = 0; start < outgoing.Count; start += MaxPerMessage) for (int start = 0; start < outgoing.Count; start += MaxPerMessage)
@@ -176,13 +181,16 @@ namespace MSCO
return false; return false;
} }
static bool Changed(Var v) static bool Changed(Var v, bool bigOnly)
{ {
switch (v.Type) switch (v.Type)
{ {
case 1: case 1:
float f = ((FsmFloat)v.Variable).Value; float f = ((FsmFloat)v.Variable).Value;
return Mathf.Abs(f - v.LastF) > Mathf.Max(0.01f, Mathf.Abs(v.LastF) * 0.002f); float delta = Mathf.Abs(f - v.LastF);
return bigOnly
? delta > Mathf.Max(0.5f, Mathf.Abs(v.LastF) * 0.05f)
: delta > Mathf.Max(0.01f, Mathf.Abs(v.LastF) * 0.002f);
case 2: return ((FsmInt)v.Variable).Value != v.LastI; case 2: return ((FsmInt)v.Variable).Value != v.LastI;
default: return ((FsmBool)v.Variable).Value != v.LastB; default: return ((FsmBool)v.Variable).Value != v.LastB;
} }